Terraform 備忘録

# Terraformとは？
IaCの一種です。AWS・GCP・Azureなど、様々なクラウドで使用することが可能です。


# IaCとは？
IaC (Infrastructure as Code) とは、インフラリソースをコードで管理することです。
各クラウドで、コンソールをポチポチと押してインフラリソースを作成せずとも、コードさえ書いてしまえば、簡単にリソースが作成できます。

# IaCのメリット
私は業務でTerraformを使用しているのですが、その中で感じたメリットは以下です。

**・新たな環境を即座に作成できる**
一度コードを用意すれば、別環境で同じリソースを作成する際に、簡単に、即座にリソースを作成できます。

**・ヒューマンエラーが減る**
コンソールからリソースを作成すると、入力忘れ等が発生する可能性が高いです。
また、手順書を作成しても、次の日にはレイアウトが変わっていたりして、「このボタンどこ？？」みたいなことが起きる可能性もあります。
それに対して、terraformでコードを用意しておけば、terraform apply というコマンドを実行するだけでリソースが作成できるので、精度が上昇すると思います。

**・楽しい**
ただ単にコンソールをポチポチするより、コードを用意して作成する方が楽しいですし、スマートでクールな気分になれます。

# terraform でリソースを作成する流れ
**①tfファイルの作成**
Terraform ではtfファイルというファイルを使用します。
このtfファイルに作成したいリソースの設定をコードで書き込みます。

**②コマンド実行**
terraformにはいろんなコマンドがありますが、以下の3つのコマンドさえ使えれば、大抵なんとかなります。
1. terraform init
まず使用するプロバイダを指定したtfファイルを作成し、terraform initで
「今からこのクラウドでリソース作成していくよ」とterraformに
教えてあげる必要があります。

2. terraform plan
準備したtfファイルでどんなリソースができるのかを確認できます。

3. terraform apply
準備したtfファイルを実際に実行し、リソースを作成します。



**③コンソール画面でリソースが作成されたことを確認**
最後にコンソール画面でリソースが作成されたことを確認します。

# 実際にterraform でリソースを作成してみよう！
ここでは、AWSでEC2インスタンスを作成する方法を紹介します。

## 1. terraform のインストール
下記ドキュメントを参照し、OSに適した方法でterraform をインストールしてください。
https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli
## 2.tfファイルの用意
プロバイダを指定したtfファイルと、EC2インスタンスの設定をコード化したtfファイルを用意します。
以下のTerraform の公式ドキュメントに、どういう風にコードを書けばいいかのサンプルが載っているので、それを参考に記述します。
https://registry.terraform.io/providers/hashicorp/aws/latest/docs
AzureやGCPのサンプルコードも確認できます。

まず、プロバイダーの指定をします。`provider.tf`を用意します
```hcl
#使用するプロバイダーを指定
provider "aws" {
  #リージョン名指定
  region = "ap-northeast-1"
}
```



続いて、EC2の設定をコード化した`ec2.tf`を用意します
```hcl
resource "aws_instance" "web" {
  ami           = "----"
  instance_type = "t3.micro"

  tags = {
    Name = "HelloWorld"
  }
}
```

続いて、terraform initを実行し、プロバイダー指定のファイルを実行します。
```
$ terraform init

Initializing the backend...

Initializing provider plugins...
- Finding latest version of hashicorp/aws...
- Installing hashicorp/aws v5.37.0...
- Installed hashicorp/aws v5.37.0 (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を使用して、AWSでリソースを作成していく準備ができました。

## 3.tfファイルの実行
まずは、terraform plan を実行し、今のtfファイルでどんなリソースが作成されるのかを確認します。
```
$ 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.web will be created
  + resource "aws_instance" "web" {
      + ami                                  = "----"
      + 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)
      + host_resource_group_arn              = (known after apply)
      + iam_instance_profile                 = (known after apply)
      + id                                   = (known after apply)
      + instance_initiated_shutdown_behavior = (known after apply)
      + instance_lifecycle                   = (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
      + spot_instance_request_id             = (known after apply)
      + subnet_id                            = (known after apply)
      + tags                                 = {
          + "Name" = "HelloWorld"
        }
      + tags_all                             = {
          + "Name" = "HelloWorld"
        }
      + 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)
    }

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.
```

さっき用意したtfファイルで、ちゃんとEC2インスタンスが作れそうです。
最初のうちは、terraform apply を実行する前に、terraform plan でどんなリソースが作成されるのかをチェックすることをお勧めします。

その後、terraform apply を実行します。
```
[cloudshell-user@ip-10-130-63-124 terraform]$ 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.web will be created
  + resource "aws_instance" "web" {
      + ami                                  = "----"
      + 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)
      + host_resource_group_arn              = (known after apply)
      + iam_instance_profile                 = (known after apply)
      + id                                   = (known after apply)
      + instance_initiated_shutdown_behavior = (known after apply)
      + instance_lifecycle                   = (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
      + spot_instance_request_id             = (known after apply)
      + subnet_id                            = (known after apply)
      + tags                                 = {
          + "Name" = "HelloWorld"
        }
      + tags_all                             = {
          + "Name" = "HelloWorld"
        }
      + 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)
    }

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:
  ```
  terraform apply を実行するなら、Enter a Value に yes を入力します。
  キャンセルしたい場合は no を入力します。
  yes を入力します。

```
  Enter a value: yes

aws_instance.web: Creating...
aws_instance.web: Still creating... [10s elapsed]
aws_instance.web: Creation complete after 12s [id=i-05c09cec1f822990d]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
```
無事作成できたようです。
コンソール画面を確認すると、「HelloWorld」という名前のインスタンスが作成されていました。


terraform apply を実行すると、`terraform.tfstate`というファイルが作成されます。
ここに作成したリソースの情報が管理されています。
terraform はこのファイルを参照しながら、クラウド環境とtfファイルの整合性をとっています。
catで見てみると、今作成したインスタンスの情報がありました。
```
cat terraform.tfstate
{
  "version": 4,
  "terraform_version": "1.7.3",
  "serial": 5,
  "lineage": "16c20a8d-4aaa-7c1d-23da-0e969c939373",
  "outputs": {},
  "resources": [
    {
      "mode": "managed",
      "type": "aws_instance",
      "name": "web",
      "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
      "instances": [
        {
          "schema_version": 1,
          "attributes": {
            "ami": "----",
            "arn": "----",
            "associate_public_ip_address": true,
            "availability_zone": "ap-northeast-1a",
            "capacity_reservation_specification": [
              {
                "capacity_reservation_preference": "open",
                "capacity_reservation_target": []
              }
            ],
            "cpu_core_count": 1,
            "cpu_options": [
              {
                "amd_sev_snp": "",
                "core_count": 1,
                "threads_per_core": 2
              }
            ],
            "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": "",
            "host_resource_group_arn": null,
            "iam_instance_profile": "",
            "id": "i-0bdd6210aafde315b",
            "instance_initiated_shutdown_behavior": "stop",
            "instance_lifecycle": "",
            "instance_market_options": [],
            "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_protocol_ipv6": "disabled",
                "http_put_response_hop_limit": 2,
                "http_tokens": "required",
                "instance_metadata_tags": "disabled"
              }
            ],
            "monitoring": false,
            "network_interface": [],
            "outpost_arn": "",
            "password_data": "",
            "placement_group": "",
            "placement_partition_number": 0,
            "primary_network_interface_id": "eni-0a0facca11180fe23",
            "private_dns": "ip-172-31-43-170.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.43.170",
            "public_dns": "ec2-52-194-251-61.ap-northeast-1.compute.amazonaws.com",
            "public_ip": "52.194.251.61",
            "root_block_device": [
              {
                "delete_on_termination": true,
                "device_name": "/dev/xvda",
                "encrypted": false,
                "iops": 3000,
                "kms_key_id": "",
                "tags": {},
                "throughput": 125,
                "volume_id": "vol-0cf2036cd1616dc39",
                "volume_size": 8,
                "volume_type": "gp3"
              }
            ],
            "secondary_private_ips": [],
            "security_groups": [
              "default"
            ],
            "source_dest_check": true,
            "spot_instance_request_id": "",
            "subnet_id": "subnet-094c7100bfa7dca28",
            "tags": {
              "Name": "HelloWorld"
            },
            "tags_all": {
              "Name": "HelloWorld"
            },
            "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-062e0b708fa47fda1"
            ]
          },
          "sensitive_attributes": [],
          "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6MTIwMDAwMDAwMDAwMCwidXBkYXRlIjo2MDAwMDAwMDAwMDB9LCJzY2hlbWFfdmVyc2lvbiI6IjEifQ=="
        }
      ]
    }
  ],
  "check_results": null
}
```

## 4. 作成したリソースを削除する
削除方法は簡単です。作成したリソースのコードをコメントアウトし、再度terraform applyを実行します。
```hcl
/*
resource "aws_instance" "web" {
  ami           = "----"
  instance_type = "t3.micro"

  tags = {
    Name = "HelloWorld"
  }
}
*/
```
`ec2.tf`をコメントアウトしたのち、terraform apply を実行します。

```
$ terraform apply
aws_instance.web: Refreshing state... [id=i-05c09cec1f822990d]

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.web will be destroyed
  # (because aws_instance.web is not in configuration)
  - resource "aws_instance" "web" {
      - ami                                  = "----" -> null
      - arn                                  = "----" -> null
      - associate_public_ip_address          = true -> null
      - availability_zone                    = "ap-northeast-1a" -> 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-05c09cec1f822990d" -> 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
      - placement_partition_number           = 0 -> null
      - primary_network_interface_id         = "eni-0ad53bbabeb15802a" -> null
      - private_dns                          = "ip-172-31-38-43.ap-northeast-1.compute.internal" -> null
      - private_ip                           = "172.31.38.43" -> null
      - public_dns                           = "ec2-54-250-216-133.ap-northeast-1.compute.amazonaws.com" -> null
      - public_ip                            = "54.250.216.133" -> null
      - secondary_private_ips                = [] -> null
      - security_groups                      = [
          - "default",
        ] -> null
      - source_dest_check                    = true -> null
      - subnet_id                            = "subnet-094c7100bfa7dca28" -> null
      - tags                                 = {
          - "Name" = "HelloWorld"
        } -> null
      - tags_all                             = {
          - "Name" = "HelloWorld"
        } -> null
      - tenancy                              = "default" -> null
      - user_data_replace_on_change          = false -> null
      - vpc_security_group_ids               = [
          - "sg-062e0b708fa47fda1",
        ] -> null

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

      - cpu_options {
          - core_count       = 1 -> null
          - threads_per_core = 2 -> 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_protocol_ipv6          = "disabled" -> null
          - http_put_response_hop_limit = 2 -> null
          - http_tokens                 = "required" -> 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                  = 3000 -> null
          - tags                  = {} -> null
          - throughput            = 125 -> null
          - volume_id             = "vol-0f268ea34b8a7168e" -> null
          - volume_size           = 8 -> null
          - volume_type           = "gp3" -> null
        }
    }

Plan: 0 to add, 0 to change, 1 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.web: Destroying... [id=i-05c09cec1f822990d]
aws_instance.web: Still destroying... [id=i-05c09cec1f822990d, 10s elapsed]
aws_instance.web: Still destroying... [id=i-05c09cec1f822990d, 20s elapsed]
aws_instance.web: Still destroying... [id=i-05c09cec1f822990d, 30s elapsed]
aws_instance.web: Still destroying... [id=i-05c09cec1f822990d, 40s elapsed]
aws_instance.web: Still destroying... [id=i-05c09cec1f822990d, 50s elapsed]
aws_instance.web: Destruction complete after 50s

Apply complete! Resources: 0 added, 0 changed, 1 destroyed.
```
これで削除完了です。
cat で `terraform.tfstate`を見てみると、先ほどあったec2インスタンスの情報が削除されていました。
```
#terraformバージョンの記述のみになっている
$ cat terraform.tfstate
{
  "version": 4,
  "terraform_version": "1.7.3",
  "serial": 7,
  "lineage": "16c20a8d-4aaa-7c1d-23da-0e969c939373",
  "outputs": {},
  "resources": [],
  "check_results": null
}
```

以上がterraformの使い方です。