Terraform公式チュートリアル実況プレイ

### ①コマンドのインストール

homebrewでインストールできた。

``` 
brew tap hashicorp/tap
brew install hashicorp/tap/terraform
```



### ②コマンドラインの設定

チュートリアルだとパスを通したり、コマンドの補完の設定をしているが自分の場合zshで履歴に基づいて予測候補を表示できるようにしているのでここはパス

### ③DockerをTerraformで動かす

ここから実践的な感じで、Dockerを実際にTerraformで動かして見る。まずはディレクトリの作成

```
open -a Docker
mkdir learn-terraform-docker-container
cd learn-terraform-docker-container
```

んで、 `main.tf` というファイルを作る。これがTerraformのメインの設定ファイルになるっぽい。

```tf
terraform {
  required_providers {
    docker = {
      source  = "kreuzwerker/docker"
      version = "~> 2.13.0"
    }
  }
}

provider "docker" {}

resource "docker_image" "nginx" {
  name         = "nginx:latest"
  keep_locally = false
}

resource "docker_container" "nginx" {
  image = docker_image.nginx.latest
  name  = "tutorial"
  ports {
    internal = 80
    external = 8000
  }
}
```

んで、これを書いた状態で↓を実行するとプロジェクトが立ち上がる。(この状態ではまだリソースは動いていない)

```
terraform init
```

で、↓を実行するとリソースが実際に立ち上がる。

```
terraform apply
```

今回の場合はnginxのイメージを持っているDockerコンテナを立ち上げるような設定にしている。またポートを80:8000にしているので、localhost:8000でnginxのサーバーに接続できる。

立ち上げたリソースを停止するには↓を実行

```
terraform destroy
```

### ④AWSリソースをTerraformで立ち上げる

TerraformでAWSのリソースを立ち上げて見る。

Dockerの時と同様に `main.tf` に設定を書く。

```tf
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.16"
    }
  }

  required_version = ">= 1.2.0"
}

provider "aws" {
  region  = "us-west-2"
}

resource "aws_instance" "app_server" {
  ami           = "ami-830c94e3"
  instance_type = "t2.micro"

  tags = {
    Name = "ExampleAppServerInstance"
  }
}

```

おそらく、チュートリアルドキュメントを見る限り各設定項目の意図は↓の通り

- required_providers...Terraformを使う上で必須となるprovider。sourceでproviderの存在する場所、versionで利用するpvoviderのバージョンを指定している

- provider...Terraformでリソースを管理するプラットフォーム的な何か。Docker、AWS、GCPみたいな粒度の値が入る。
- resources...provider上で実際に立ち上げるリソース。これはシンプルだね

設定ファイルを書いたら先ほど同様に↓で立ち上げる。

```
terraform init
terraform apply
```

ちなみに。設定ファイルはフォーマットや記述ミスのチェックも可能で↓で行う。CIとかで使えそうだね。

```
terraform fmt
terraform validate
```

なんかエラーに遭遇したのでメモ

```
terraform apply
```

をすると↓のエラー

```
aws_instance.app_server: Creating...
╷
│ Error: creating EC2 Instance: InvalidAMIID.NotFound: The image id '[ami-830c94e3]' does not exist
│ 	status code: 400, request id: 24596a32-6f55-49d6-b205-94a0bf5078f1
│ 
│   with aws_instance.app_server,
│   on main.tf line 16, in resource "aws_instance" "app_server":
│   16: resource "aws_instance" "app_server" {
│ 
╵

```

見た感じAMIがないよ！って言ってるのでいろいろ調べてたところ、リージョンによってamiのIDが異なるっぽい。
チュートリアルの `main.tf` で `us-west-2` のリージョンが使われていたが、当然お金かけたくないので `ap-northeast-1` に変更した。ただ、amiのIDはそのままにしていたのでエラーになったっぽい。

```
aws ec2 describe-images --owners self amazon --region ap-northeast-1
```

AWS CLIでこんな感じのコマンドを叩くと東京リージョンでawsm公式が持っているAMIが取得できる。と思ったんだけど取得結果が見づらいのでコンソールから調べて見る。

EC2インスタンスを立ち上げるときに↓みたいな画面になるが、ここで無料利用枠の対象となるイメージを選択。

![Cursor_と_インスタンスを起動___EC2_Management_Console.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/652172/a62e074a-c06a-e0ca-b76e-7b7bedb0e1f1.png)

AMIのIDがあるのでそれを `main.tf` にコピーして見る。

よし。無事 `terraform apply` に成功。やったぜ。

![Cursor_と_インスタンス___EC2_Management_Console.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/652172/ce8a5829-58ff-19d0-0a0c-170b61d1f4f3.png)

実際にコンソール見たら立ち上がってた。すごい。

ちなみに立ち上げたリソースの詳細をチェックするには↓を実行する。

```
terraform show
```

↓みたいな結果が表示されればOK

```
resource "aws_instance" "app_server" {
    ami                                  = "ami-078296f82eb463377"
    arn                                  = "..."
    associate_public_ip_address          = true
    availability_zone                    = "ap-northeast-1c"
    cpu_core_count                       = 1
    cpu_threads_per_core                 = 1
    disable_api_stop                     = false
    disable_api_termination              = false
    ebs_optimized                        = false
    get_password_data                    = false
    hibernation                          = false
    id                                   = "i-0413a991b88bc950e"
    instance_initiated_shutdown_behavior = "stop"
    instance_state                       = "running"
    instance_type                        = "t2.micro"
    ipv6_address_count                   = 0
    ipv6_addresses                       = []
    monitoring                           = false
    primary_network_interface_id         = "eni-04ae1cf226d4d833d"
    private_dns                          = "ip-172-31-3-118.ap-northeast-1.compute.internal"
    private_ip                           = "172.31.3.118"
    public_dns                           = "ec2-43-206-106-51.ap-northeast-1.compute.amazonaws.com"
    public_ip                            = "43.206.106.51"
    secondary_private_ips                = []
    security_groups                      = [
        "default",
    ]
    source_dest_check                    = true
    subnet_id                            = "subnet-0a658c50"
    tags                                 = {
        "Name" = "ExampleAppServerInstance"
    }
    tags_all                             = {
        "Name" = "ExampleAppServerInstance"
    }
    tenancy                              = "default"
    user_data_replace_on_change          = false
    vpc_security_group_ids               = [
        "sg-759f0a37",
    ]

    capacity_reservation_specification {
        capacity_reservation_preference = "open"
    }

    credit_specification {
        cpu_credits = "standard"
    }

    enclave_options {
        enabled = false
    }

    maintenance_options {
        auto_recovery = "default"
    }

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

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

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

一旦ここでコミットしておきましょか。

ちなみに設定の変更を反映するときも `terraform apply` でOK

### インフラを止める

立ち上げたリソースを停止したい場合は↓を実行

```
terraform destroy
```

![Cursor_と_インスタンス___EC2_Management_Console.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/652172/68b8653a-aa2e-15b7-ba6c-897068aa9122.png)

さっき起動中になっていたリソースが終了済みになった。

### 変数を渡す

terraformの設定ファイルに変数を渡して、コマンド実行ごとに違う値を渡すことができる。

`variables.tf` を作成して↓の通り記述

```tf
variable "instance_name" {
  description = "Value of the Name tag for the EC2 instance"
  type        = string
  default     = "ExampleAppServerInstance"
}
```

変数を設定ファイルに渡すときは↓のように書く

```tf
resource "aws_instance" "app_server" {
  ami           = "ami-078296f82eb463377"
  instance_type = "t2.micro"

  tags = {
    Name = var.instance_name
  }
}
```

`var.instance_name` とすると、`instance_name` という変数の値をtagsのNameに渡すことができる。

変数に `default     = "ExampleAppServerInstance"` としているので、特に値を渡さなければdefaultの値でリソースが立ち上がる。

ちなみにここで特にinstance IDを指定していないからなのか、先ほどとは違うインスタンスが立ち上がった。(一度destroyしていると違うリソースが立ち上がる？)

次にコマンド実行時に変数を渡す。

```
terraform apply -var "instance_name=YetAnotherName"
```

こうすると、以下のようなログが流れNameの値が変わったことがわかる。

```
Terraform will perform the following actions:

  # aws_instance.app_server will be updated in-place
  ~ resource "aws_instance" "app_server" {
        id                                   = "i-07106c4743876f6cb"
      ~ tags                                 = {
          ~ "Name" = "ExampleAppServerInstance" -> "YetAnotherName"
        }
      ~ tags_all                             = {
          ~ "Name" = "ExampleAppServerInstance" -> "YetAnotherName"
        }
        # (29 unchanged attributes hidden)

        # (7 unchanged blocks hidden)
    }

Plan: 0 to add, 1 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
```

実際にコンソール上でも値が変わっていることがわかる。

![Cursor_と_インスタンス___EC2_Management_Console.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/652172/98eeefb1-0247-33d5-e222-577d00169d2b.png)

### リソース情報を出力できるようにする

Terraform上でどんなリソースが立ち上がっているのかをコマンドラインから確認できるようにする。
こうすると共同作業者が使いやすかったりAWSコンソールから探しやすかったりのメリットがありそう。

`outputs.tf` を作成し、↓のように記述

```tf
output "instance_id" {
  description = "ID of the EC2 instance"
  value       = aws_instance.app_server.id
}

output "instance_public_ip" {
  description = "Public IP address of the EC2 instance"
  value       = aws_instance.app_server.public_ip
}
```

設定内容は↓のコマンドで確認できる。

```
terraform output
```

実行結果

```
tomolico@mourifuyusatorunoMacBook-Air learn-terraform-aws-instance % terraform output
instance_id = "i-07106c4743876f6cb"
instance_public_ip = "54.65.225.171"
```

### Terraform Cloudにリソース情報を保存

terraformのコードから作られるリソース情報をcloud上に管理しておくことができる。GUIから見れるっぽくて便利だね。

`maim.tf` に↓の記述を追加

```tf
cloud {
    organization = "organization-name"
    workspaces {
      name = "learn-tfc-aws"
    }
  }
```

origanizationはサインインするときに設定する。nameは任意の名前でOK。

CLIとterraform cloudを連携する。↓のコマンドを実行。

```
terraform login
```

↓のようにAPIのtokenを入力するように求められるので、web上で発行したトークンを入力する。

```
Generate a token using your browser, and copy-paste it into this prompt.

Terraform will store the token in plain text in the following file
for use by subsequent commands:
    /Users/tomolico/.terraform.d/credentials.tfrc.json

Token for app.terraform.io:
  Enter a value: 
```

![Cursor_と_Tokens___Account___Terraform_Cloud.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/652172/e275d38b-a561-71a3-0968-569fd8333e7f.png)

ちなみにトークンはweb上で個別発行もできる。

そのまま立ち上げようとすると以下のようなエラーに遭遇。どうやら環境変数でAWSのクレデンシャルを渡さないといけないらしい。

```
Initializing plugins and modules...
╷
│ Error: error configuring Terraform AWS Provider: no valid credential sources for Terraform AWS Provider found.
│ 
│ Please see https://registry.terraform.io/providers/hashicorp/aws
│ for more information about providing credentials.
```

![Cursor_と_Variables___aws-tutorial___moritania___Terraform_Cloud.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/652172/d5f88c83-19ef-96ba-9381-9ac2f2d526f7.png)

workspaceのvariablesってところで設定できるので設定しておく。ちなみにこの画面に辿り着くには先に空のワークスペースを立ち上げておく必要がありそう。おそらくcloudで接続していないときはデフォルトでコマンドラインで設定した環境変数を読み込む設定になっていたのだろうけど、cloud連携した結果読み込み先が変わったって話かなと思う。

ちなみに普通にインスタンスを立ち上げようとすると↓みたいな警告が出てくる。

```
Changes to Outputs:
  + instance_id        = (known after apply)
  + instance_public_ip = (known after apply)
╷
│ Warning: Value for undeclared variable
│ 
│ The root module does not declare a variable named "AWS_SECRET_ACCESS_KEY"
│ but a value was found in file
│ "/home/tfc-agent/.tfc-agent/component/terraform/runs/run-ufJR9wYcwnidho6D/terraform.tfvars".
│ If you meant to use this value, add a "variable" block to the
│ configuration.
│ 
│ To silence these warnings, use TF_VAR_... environment variables to provide
│ certain "global" settings to all configurations in your organization. To
│ reduce the verbosity of these warnings, use the -compact-warnings option.
╵
╷
│ Warning: Value for undeclared variable
│ 
│ The root module does not declare a variable named "AWS_ACCESS_KEY_ID" but a
│ value was found in file
│ "/home/tfc-agent/.tfc-agent/component/terraform/runs/run-ufJR9wYcwnidho6D/terraform.tfvars".
│ If you meant to use this value, add a "variable" block to the
│ configuration.
│ 
│ To silence these warnings, use TF_VAR_... environment variables to provide
│ certain "global" settings to all configurations in your organization. To
│ reduce the verbosity of these warnings, use the -compact-warnings option.
╵
```

放置しておいても動くのだけど、環境変数ファイルとかから読み込ませるなりしておいた方がいいのかな。

cloud連携した状態でリソースを立ち上げると↓みたいな状態になる。こっちのがわかりやすいのはわかりやすいわね

![Cursor_と_Overview___aws-tutorial___moritania___Terraform_Cloud.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/652172/90430549-74d3-f36e-a5e6-fe422e033205.png)


ちなみにリソースを終了すると↓の表示になる。なんとコマンドライン実行時に非同期でプルしてくれました。すごい。

![Cursor_と_Overview___aws-tutorial___moritania___Terraform_Cloud.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/652172/d8f26266-a0a5-aed0-24b2-7355dc383865.png)