Terraformを使って覚える。（その３：Terraformの実行）

# はじめに
`Terraform`の開発環境が整ったので、今回から`Terraform`を使って`AWS`への構築を進めてみようと思います。

- 【前】[Terraformを使って覚える。（その２：git-secretsとVS Codeのインストール）](https://qiita.com/sakai00kou/items/5941a4862727234e6145)

# Terraform実行方法
実際に`AWS`のEC2インスタンスを作成する例を基に`Terraform`を実行してみます。

- コーディング
- terraform initの実行（初回のみ）
- terraform fmtの実行
- terraform planの実行
- terraform applyの実行

## コーディング
前回インストールした`VS Code`でコーディングを行う場合、「**～.tf**」という拡張子にすることで`Terraform`のフォーマットだと認識されます。

EC2インスタンスを作成する場合、最低限以下のようなコーディングを行うことで作成することができます。

```hcl:main.tfの作成
provider "aws" {
  region = "ap-northeast-1"
  profile = "default"
}

resource "aws_instance" "testtf" {
    ami = "ami-0b7546e839d7ace12"
    instance_type = "t3.micro"
}
```

各種リソースで指定可能な設定やオプションは以下のオンラインドキュメントを参照することで確認できます。

- [AWS Provider](https://registry.terraform.io/providers/hashicorp/aws/latest/docs)

今回の場合、`Terraform`が暗黙で`AWS`への実行だと判断し、セットアップ済みの`AWS CLI`のデフォルトクレデンシャル情報を使用して接続しに行くため、`provider`ブロックは無くても動作しますが、意図しない動作とならないよう、明示するようにしましょう。

# terraform initの実行（初回のみ）
`terraform init`コマンドを実行することで、カレントディレクトリに`.terraform.locl.hcl`ファイルと`.terraform`フォルダが作成され、その中に各種プロバイダのリソース作成に必要となるバイナリファイルがダウンロードされます。

|ファイル・フォルダ名|説明|
|:--|:--|
|main.tf|作成したソースコード|
|.terraform|AWS等の各種プロバイダリソース作成用バイナリファイル格納|
|.terraform.lock.hcl|各種プロバイダファイルのロックファイル|

`.terraform.lock.hcl`は以下のようなファイルとなります。

実行環境が変わったとしても`Provider`の動作が変わらないようにするためのファイルとのこと。

```hcl:.terraform.lock.hclファイルの例
# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.

provider "registry.terraform.io/hashicorp/aws" {
  version = "4.20.1"
  hashes = [
    "h1:mU0Rl9mMkny1Q34aiQMSTA2R/wfVPJgHQGM+/GYUfG0=",
    "zh:21d064d8fac08376c633e002e2f36e83eb7958535e251831feaf38f51c49dafd",
    "zh:3a37912ff43d89ce8d559ec86265d7506801bccb380c7cfb896e8ff24e3fe79d",
    "zh:795eb175c85279ec51dbe12e4d1afa0860c2c0b22e5d36a8e8869f60a93b7931",
    "zh:8afb61a18b17f8ff249cb23e9d3b5d2530944001ef1d56c1d53f41b0890c7ab8",
    "zh:911701040395e0e4da4b7252279e7cf1593cdd26f22835e1a9eddbdb9691a1a7",
    "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425",
    "zh:a46d54a6a5407f569f8178e916af888b2b268f86448c64cad165dc89759c8399",
    "zh:c5f71fd5e3519a24fd6af455ef1c26a559cfdde7f626b0afbd2a73bb79f036b1",
    "zh:df3b69d6c9b0cdc7e3f90ee08412b22332c32e97ad8ce6ccad528f89f235a7d3",
    "zh:e99d6a64c03549d60c2accf792fa04466cfb317f72e895c8f67eff8a02920887",
    "zh:eea7a0df8bcb69925c9ce8e15ef403c8bbf16d46c43e8f5607b116531d1bce4a",
    "zh:f6a26ce77f7db1d50ce311e32902fd001fb365e5e45e47a9a5cd59d734c89cb6",
  ]
}
```

また、`terraform init`を実行した場合、以下のような結果が出力され、各種プロバイダリソース作成用バイナリのダウンロードや初期化が行われます。

```hcl:terraform_init実行時の結果例
$ terraform init

Initializing the backend...

Initializing provider plugins...
- Finding latest version of hashicorp/aws...
- Installing hashicorp/aws v4.20.1...
- Installed hashicorp/aws v4.20.1 (signed by HashiCorp)

Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
```

## terraform fmtの実行
`terraform fmt`は毎回実行しなくても良いですが、`terraform fmt`を実行すると、先ほどコーディングしたソースファイルのフォーマットを整えてくれるため、コーディング後には実行したほうがよいでしょう。

```hcl:terraform_fmtの実行
terraform fmt
```

ちなみに先ほどコーディングした`main.tf`に`terraform fmt`コマンドを実行した場合、以下のように整形されます。

```hcl:terraform_fmt実行後のmain.tf
provider "aws" {
  region  = "ap-northeast-1"
  profile = "default"
}

resource "aws_instance" "testtf" {
  ami           = "ami-0b7546e839d7ace12"
  instance_type = "t3.micro"
}
```

## terraform planの実行
`terraform plan`を実行すると、どのようなリソースが作成・変更・削除されるかといった情報を確認することができます。

確認だけで作成はされないため、次の`terraform apply`コマンドを実行する前の確認用途で使います。

以下「**+**」で表示されるリソースは、`terraform apply`実行時に作成されるリソース、（今回はありませんが）「**~**」で表示されるリソースは`terraform apply`実行時に削除されるリソースとなります。

<details><summary><strong>terraform planの実行結果（展開してください）</strong></summary>

```hcl:terraform_planの実行
$ terraform plan

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_instance.testtf will be created
  + resource "aws_instance" "testtf" {
      + ami                                  = "ami-0b7546e839d7ace12"
      + arn                                  = (known after apply)
      + associate_public_ip_address          = (known after apply)
      + availability_zone                    = (known after apply)
      + cpu_core_count                       = (known after apply)
      + cpu_threads_per_core                 = (known after apply)
      + disable_api_stop                     = (known after apply)
      + disable_api_termination              = (known after apply)
      + ebs_optimized                        = (known after apply)
      + get_password_data                    = false
      + host_id                              = (known after apply)
      + id                                   = (known after apply)
      + instance_initiated_shutdown_behavior = (known after apply)
      + instance_state                       = (known after apply)
      + instance_type                        = "t3.micro"
      + ipv6_address_count                   = (known after apply)
      + ipv6_addresses                       = (known after apply)
      + key_name                             = (known after apply)
      + monitoring                           = (known after apply)
      + outpost_arn                          = (known after apply)
      + password_data                        = (known after apply)
      + placement_group                      = (known after apply)
      + placement_partition_number           = (known after apply)
      + primary_network_interface_id         = (known after apply)
      + private_dns                          = (known after apply)
      + private_ip                           = (known after apply)
      + public_dns                           = (known after apply)
      + public_ip                            = (known after apply)
      + secondary_private_ips                = (known after apply)
      + security_groups                      = (known after apply)
      + source_dest_check                    = true
      + subnet_id                            = (known after apply)
      + tags_all                             = (known after apply)
      + tenancy                              = (known after apply)
      + user_data                            = (known after apply)
      + user_data_base64                     = (known after apply)
      + user_data_replace_on_change          = false
      + vpc_security_group_ids               = (known after apply)

      + capacity_reservation_specification {
          + capacity_reservation_preference = (known after apply)

          + capacity_reservation_target {
              + capacity_reservation_id                 = (known after apply)
              + capacity_reservation_resource_group_arn = (known after apply)
            }
        }

      + ebs_block_device {
          + delete_on_termination = (known after apply)
          + device_name           = (known after apply)
          + encrypted             = (known after apply)
          + iops                  = (known after apply)
          + kms_key_id            = (known after apply)
          + snapshot_id           = (known after apply)
          + tags                  = (known after apply)
          + throughput            = (known after apply)
          + volume_id             = (known after apply)
          + volume_size           = (known after apply)
          + volume_type           = (known after apply)
        }

      + enclave_options {
          + enabled = (known after apply)
        }

      + ephemeral_block_device {
          + device_name  = (known after apply)
          + no_device    = (known after apply)
          + virtual_name = (known after apply)
        }

      + maintenance_options {
          + auto_recovery = (known after apply)
        }

      + metadata_options {
          + http_endpoint               = (known after apply)
          + http_put_response_hop_limit = (known after apply)
          + http_tokens                 = (known after apply)
          + instance_metadata_tags      = (known after apply)
        }

      + network_interface {
          + delete_on_termination = (known after apply)
          + device_index          = (known after apply)
          + network_card_index    = (known after apply)
          + network_interface_id  = (known after apply)
        }

      + private_dns_name_options {
          + enable_resource_name_dns_a_record    = (known after apply)
          + enable_resource_name_dns_aaaa_record = (known after apply)
          + hostname_type                        = (known after apply)
        }

      + root_block_device {
          + delete_on_termination = (known after apply)
          + device_name           = (known after apply)
          + encrypted             = (known after apply)
          + iops                  = (known after apply)
          + kms_key_id            = (known after apply)
          + tags                  = (known after apply)
          + throughput            = (known after apply)
          + volume_id             = (known after apply)
          + volume_size           = (known after apply)
          + volume_type           = (known after apply)
        }
    }

Plan: 1 to add, 0 to change, 0 to destroy.

────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 

Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.
```
</details>

## terraform applyの実行
`terraform apply`を実行すると、コードの内容が実行されて、各プロバイダ環境（今回は`AWS`）に各種リソースが作成されます。

また、途中でリソース作成確認があり、入力を求められるため、「**yes**」と入力して実行します。

自動化等を行っており、「**yes**」の入力を省きたい場合は`-auto-approve`オプションを追加して実行すると、入力を省くことができます。

<details><summary><strong>terraform applyの実行結果（展開してください）</strong></summary>

```hcl:terraform_applyの実行
$ terraform apply

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_instance.testtf will be created
  + resource "aws_instance" "testtf" {
      + ami                                  = "ami-0b7546e839d7ace12"
      + arn                                  = (known after apply)
      + associate_public_ip_address          = (known after apply)
      + availability_zone                    = (known after apply)
      + cpu_core_count                       = (known after apply)
      + cpu_threads_per_core                 = (known after apply)
      + disable_api_stop                     = (known after apply)
      + disable_api_termination              = (known after apply)
      + ebs_optimized                        = (known after apply)
      + get_password_data                    = false
      + host_id                              = (known after apply)
      + id                                   = (known after apply)
      + instance_initiated_shutdown_behavior = (known after apply)
      + instance_state                       = (known after apply)
      + instance_type                        = "t3.micro"
      + ipv6_address_count                   = (known after apply)
      + ipv6_addresses                       = (known after apply)
      + key_name                             = (known after apply)
      + monitoring                           = (known after apply)
      + outpost_arn                          = (known after apply)
      + password_data                        = (known after apply)
      + placement_group                      = (known after apply)
      + placement_partition_number           = (known after apply)
      + primary_network_interface_id         = (known after apply)
      + private_dns                          = (known after apply)
      + private_ip                           = (known after apply)
      + public_dns                           = (known after apply)
      + public_ip                            = (known after apply)
      + secondary_private_ips                = (known after apply)
      + security_groups                      = (known after apply)
      + source_dest_check                    = true
      + subnet_id                            = (known after apply)
      + tags_all                             = (known after apply)
      + tenancy                              = (known after apply)
      + user_data                            = (known after apply)
      + user_data_base64                     = (known after apply)
      + user_data_replace_on_change          = false
      + vpc_security_group_ids               = (known after apply)

      + capacity_reservation_specification {
          + capacity_reservation_preference = (known after apply)

          + capacity_reservation_target {
              + capacity_reservation_id                 = (known after apply)
              + capacity_reservation_resource_group_arn = (known after apply)
            }
        }

      + ebs_block_device {
          + delete_on_termination = (known after apply)
          + device_name           = (known after apply)
          + encrypted             = (known after apply)
          + iops                  = (known after apply)
          + kms_key_id            = (known after apply)
          + snapshot_id           = (known after apply)
          + tags                  = (known after apply)
          + throughput            = (known after apply)
          + volume_id             = (known after apply)
          + volume_size           = (known after apply)
          + volume_type           = (known after apply)
        }

      + enclave_options {
          + enabled = (known after apply)
        }

      + ephemeral_block_device {
          + device_name  = (known after apply)
          + no_device    = (known after apply)
          + virtual_name = (known after apply)
        }

      + maintenance_options {
          + auto_recovery = (known after apply)
        }

      + metadata_options {
          + http_endpoint               = (known after apply)
          + http_put_response_hop_limit = (known after apply)
          + http_tokens                 = (known after apply)
          + instance_metadata_tags      = (known after apply)
        }

      + network_interface {
          + delete_on_termination = (known after apply)
          + device_index          = (known after apply)
          + network_card_index    = (known after apply)
          + network_interface_id  = (known after apply)
        }

      + private_dns_name_options {
          + enable_resource_name_dns_a_record    = (known after apply)
          + enable_resource_name_dns_aaaa_record = (known after apply)
          + hostname_type                        = (known after apply)
        }

      + root_block_device {
      + 
          + delete_on_termination = (known after apply)
          + device_name           = (known after apply)
          + encrypted             = (known after apply)
          + iops                  = (known after apply)
          + kms_key_id            = (known after apply)
          + tags                  = (known after apply)
          + throughput            = (known after apply)
          + volume_id             = (known after apply)
          + volume_size           = (known after apply)
          + volume_type           = (known after apply)
        }
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_instance.testtf: Creating...
aws_instance.testtf: Still creating... [10s elapsed]
aws_instance.testtf: Creation complete after 13s [id=i-058f9caf6b5fabfd0]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
```
</details>

また、`terraform apply`実行後、カレントディレクトリに`terraform.tfstate`というファイルが作成されます。

`terraform.tfstate`ファイルは`terraform apply`実行時の各プロバイダ（今回は`AWS`）環境の状態を記録しているファイルとなり、次回`terraform apply`実行時には、`terraform.tfstate`ファイルに記載されている環境情報と比較し、差分のみを追加＆削除等を行う動作となります。

<details><summary><strong>terraform.tfstateファイルの表示例（展開してください）</strong></summary>

```hcl:terraform.tfstateファイル
{
  "version": 4,
  "terraform_version": "1.2.3",
  "serial": 1,
  "lineage": "f0456b47-464f-3c3f-e246-4aeea30425b8",
  "outputs": {},
  "resources": [
    {
      "mode": "managed",
      "type": "aws_instance",
      "name": "testtf",
      "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
      "instances": [
        {
          "schema_version": 1,
          "attributes": {
            "ami": "ami-0b7546e839d7ace12",
            "arn": "arn:aws:ec2:ap-northeast-1:335417439444:instance/i-058f9caf6b5fabfd0",
            "associate_public_ip_address": true,
            "availability_zone": "ap-northeast-1d",
            "capacity_reservation_specification": [
              {
                "capacity_reservation_preference": "open",
                "capacity_reservation_target": []
              }
            ],
            "cpu_core_count": 1,
            "cpu_threads_per_core": 2,
            "credit_specification": [
              {
                "cpu_credits": "unlimited"
              }
            ],
            "disable_api_stop": false,
            "disable_api_termination": false,
            "ebs_block_device": [],
            "ebs_optimized": false,
            "enclave_options": [
              {
                "enabled": false
              }
            ],
            "ephemeral_block_device": [],
            "get_password_data": false,
            "hibernation": false,
            "host_id": null,
            "iam_instance_profile": "",
            "id": "i-058f9caf6b5fabfd0",
            "instance_initiated_shutdown_behavior": "stop",
            "instance_state": "running",
            "instance_type": "t3.micro",
            "ipv6_address_count": 0,
            "ipv6_addresses": [],
            "key_name": "",
            "launch_template": [],
            "maintenance_options": [
              {
                "auto_recovery": "default"
              }
            ],
            "metadata_options": [
              {
                "http_endpoint": "enabled",
                "http_put_response_hop_limit": 1,
                "http_tokens": "optional",
                "instance_metadata_tags": "disabled"
              }
            ],
            "monitoring": false,
            "network_interface": [],
            "outpost_arn": "",
            "password_data": "",
            "placement_group": "",
            "placement_partition_number": null,
            "primary_network_interface_id": "eni-00a399526fcba5d63",
            "private_dns": "ip-172-31-21-72.ap-northeast-1.compute.internal",
            "private_dns_name_options": [
              {
                "enable_resource_name_dns_a_record": false,
                "enable_resource_name_dns_aaaa_record": false,
                "hostname_type": "ip-name"
              }
            ],
            "private_ip": "172.31.21.72",
            "public_dns": "ec2-35-78-254-110.ap-northeast-1.compute.amazonaws.com",
            "public_ip": "35.78.254.110",
            "root_block_device": [
              {
                "delete_on_termination": true,
                "device_name": "/dev/xvda",
                "encrypted": false,
                "iops": 100,
                "kms_key_id": "",
                "tags": {},
                "throughput": 0,
                "volume_id": "vol-0eb3884c49cb32c24",
                "volume_size": 8,
                "volume_type": "gp2"
              }
            ],
            "secondary_private_ips": [],
            "security_groups": [
              "default"
            ],
            "source_dest_check": true,
            "subnet_id": "subnet-c50cc9ee",
            "tags": null,
            "tags_all": {},
            "tenancy": "default",
            "timeouts": null,
            "user_data": null,
            "user_data_base64": null,
            "user_data_replace_on_change": false,
            "volume_tags": null,
            "vpc_security_group_ids": [
              "sg-70df8a0e"
            ]
          },
          "sensitive_attributes": [],
          "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6MTIwMDAwMDAwMDAwMCwidXBkYXRlIjo2MDAwMDAwMDAwMDB9LCJzY2hlbWFfdmVyc2lvbiI6IjEifQ=="
        }
      ]
    }
  ]
}
```
</details>

尚、デフォルトではローカルのカレントディレクトリに作成されますが、複数人で開発したり、複数端末で開発するような場合、それぞれのユーザのローカルで別々に管理されてしまうと、実行するユーザによって挙動が変わってきてしまうため、その場合はローカルではなく、リモートのストレージで管理する必要があります。

# リソース削除方法
作成したリソースをまとめて削除したい場合、`terraform destroy`コマンドを実行することで、環境をすべて削除することができます。

削除時にもリソース削除確認があり、入力が求められるため、「**yes**」と入力して実行します。

`terraform destroy`実行時にも`-auto-approve`オプションを使用できるため、入力を省きたい場合はオプションを追加して実行します。

<details><summary><strong>terraform destroyの実行結果（展開してください）</strong></summary>

```hcl:terraform_destroyの実行
$ terraform destroy
aws_instance.testtf: Refreshing state... [id=i-058f9caf6b5fabfd0]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  - destroy

Terraform will perform the following actions:

  # aws_instance.testtf will be destroyed
  - resource "aws_instance" "testtf" {
      - ami                                  = "ami-0b7546e839d7ace12" -> null
      - arn                                  = "arn:aws:ec2:ap-northeast-1:335417439444:instance/i-058f9caf6b5fabfd0" -> null
      - associate_public_ip_address          = true -> null
      - availability_zone                    = "ap-northeast-1d" -> null
      - cpu_core_count                       = 1 -> null
      - cpu_threads_per_core                 = 2 -> null
      - disable_api_stop                     = false -> null
      - disable_api_termination              = false -> null
      - ebs_optimized                        = false -> null
      - get_password_data                    = false -> null
      - hibernation                          = false -> null
      - id                                   = "i-058f9caf6b5fabfd0" -> null
      - instance_initiated_shutdown_behavior = "stop" -> null
      - instance_state                       = "running" -> null
      - instance_type                        = "t3.micro" -> null
      - ipv6_address_count                   = 0 -> null
      - ipv6_addresses                       = [] -> null
      - monitoring                           = false -> null
      - primary_network_interface_id         = "eni-00a399526fcba5d63" -> null
      - private_dns                          = "ip-172-31-21-72.ap-northeast-1.compute.internal" -> null
      - private_ip                           = "172.31.21.72" -> null
      - public_dns                           = "ec2-35-78-254-110.ap-northeast-1.compute.amazonaws.com" -> null
      - public_ip                            = "35.78.254.110" -> null
      - secondary_private_ips                = [] -> null
      - security_groups                      = [
          - "default",
        ] -> null
      - source_dest_check                    = true -> null
      - subnet_id                            = "subnet-c50cc9ee" -> null
      - tags                                 = {} -> null
      - tags_all                             = {} -> null
      - tenancy                              = "default" -> null
      - user_data_replace_on_change          = false -> null
      - vpc_security_group_ids               = [
          - "sg-70df8a0e",
        ] -> null

      - capacity_reservation_specification {
          - capacity_reservation_preference = "open" -> null
        }

      - credit_specification {
          - cpu_credits = "unlimited" -> null
        }

      - enclave_options {
          - enabled = false -> null
        }

      - maintenance_options {
          - auto_recovery = "default" -> null
        }

      - metadata_options {
          - http_endpoint               = "enabled" -> null
          - http_put_response_hop_limit = 1 -> null
          - http_tokens                 = "optional" -> null
          - instance_metadata_tags      = "disabled" -> null
        }

      - private_dns_name_options {
          - enable_resource_name_dns_a_record    = false -> null
          - enable_resource_name_dns_aaaa_record = false -> null
          - hostname_type                        = "ip-name" -> null
        }

      - root_block_device {
          - delete_on_termination = true -> null
          - device_name           = "/dev/xvda" -> null
          - encrypted             = false -> null
          - iops                  = 100 -> null
          - tags                  = {} -> null
          - throughput            = 0 -> null
          - volume_id             = "vol-0eb3884c49cb32c24" -> null
          - volume_size           = 8 -> null
          - volume_type           = "gp2" -> null
        }
    }

Plan: 0 to add, 0 to change, 1 to destroy.

Do you really want to destroy all resources?
  Terraform will destroy all your managed infrastructure, as shown above.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value: yes

aws_instance.testtf: Destroying... [id=i-058f9caf6b5fabfd0]
aws_instance.testtf: Still destroying... [id=i-058f9caf6b5fabfd0, 10s elapsed]
aws_instance.testtf: Still destroying... [id=i-058f9caf6b5fabfd0, 20s elapsed]
aws_instance.testtf: Still destroying... [id=i-058f9caf6b5fabfd0, 30s elapsed]
aws_instance.testtf: Still destroying... [id=i-058f9caf6b5fabfd0, 40s elapsed]
aws_instance.testtf: Still destroying... [id=i-058f9caf6b5fabfd0, 50s elapsed]
aws_instance.testtf: Destruction complete after 50s

Destroy complete! Resources: 1 destroyed.
```
</details>

# おわりに
今回は`Terraform`で構築した際の流れを覚えるため、各種基本操作を実行してみました。

次回は`Terraform`でコーディングする際の基本構文と、構文等調べる際に参照する各種ドキュメントについて学んでいきます。