terraform完全に理解する

**デジタルキューブ ＆ ヘプタゴン Advent Calendar 2022** の12/23分の投稿です。

https://qiita.com/advent-calendar/2022/digitalcube-heptagon

terraformを使ったことがなかったので、今更かと思うかもしれませんがこのタイミングで ~~入門~~ 完全に理解します。

# やること
terraformのチュートリアルを通してec2を作成/更新/破棄する
以下チュートリアルとは一部内容が異なる部分もある

https://developer.hashicorp.com/terraform/tutorials/aws-get-started

# ステータス

僕のIaC周辺の知見は以下

- cloudformation完全に理解した
- aws cdk完全に理解した
- chefやansibleなどの構成管理ツールは使ったことがない

# 完全に理解する

## terraformとは

IaCツールのひとつで、aws/google cloud/azureをはじめとした様々なプロバイダに対応している

https://www.terraform.io/

## ローカル環境構築

ローカル環境の構築にはasdfを使う
別で記事があるのでここでは省略

https://qiita.com/sugo/items/8b667e8b6bfe092e9d62

とりあえず最新のバージョンにしておく

```bash
% asdf latest terraform
1.3.6
% asdf install terraform latest
% asdf global terraform 1.3.6
% terraform -v
Terraform v1.3.6
on darwin_arm64
```

## 構築

### awsアカウントの準備

省略

admin権限を持ったiamユーザーを作成しprofileを登録しておく

### tfファイル

```bash
% mkdir terraform-tutorials
% cd terraform-tutorials
% touch main.tf
```

```tf:main.tf
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "4.47.0"
    }
  }
  required_version = ">= 1.2.0"
}

provider "aws" {
  region  = "us-west-2"
  profile = "sandbox-sugo"
}

resource "aws_instance" "tutorial_instance" {
  ami           = "ami-0ceecbb0f30a902a6"
  instance_type = "t2.micro"
  tags = {
    Name = "TutorialInstance"
  }
}

```

#### terraformブロック

terraform本体やプラグインの設定
使うプロバイダのプラグインをterraform registryからインストールしてよしなにやってくれる的な流れのよう
今回は「terraformのawsプラグインのこのバージョンを取ってきて使ってくれ」みたいな指令を書いてる感じ

#### providerブロック

terraformブロックで使うと宣言したawsプラグインの設定を書く

#### resourceブロック

`resource "リソース種別" "名前" {}` で定義した、管理するリソースの情報

リソース種別は `aws_vpc` や `aws_s3_bucket` などがあり、引数の指定内容なども含めて以下から確認できる

https://registry.terraform.io/providers/hashicorp/aws/latest/docs

### 実行

プラグインのダウンロード等がされる
初回は必ず実行する必要がある

```bash
% terraform init
```

コミット前などに実行してインデント等のスタイルをフォーマットする

```bash
% terraform fmt
```

構文に問題がないかチェックする

```bash
% terraform validate
```

反映内容を確認する

```bash
% terraform plan
```

`yes` を入力して反映する

```bash
% terraform apply

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.tutorial_instance: Creating...
aws_instance.tutorial_instance: Still creating... [10s elapsed]
aws_instance.tutorial_instance: Still creating... [20s elapsed]
aws_instance.tutorial_instance: Still creating... [30s elapsed]
aws_instance.tutorial_instance: Creation complete after 35s [id=i-xxxxxxxxxxxxxxxxx]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
```

マネコンからも作成されたのが確認できる

![スクリーンショット 2022-12-17 17.12.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213486/29f15947-0b4f-54c9-45c5-fa01a8625afd.png)


該当ディレクトリを見ると `terraform.tfstate` が生成されており、これの管理が一番大事っぽい
これを参照してterraformはリソースの状態を管理している
以下コマンドで現状なにがどんな状態で管理されているかを確認できる

```bash
% terraform show
```

また、以下コマンドではリソース一覧のみ見れる

```bash
% terraform state list
```

## 更新

試しにインスタンスタイプを変更してみる

```diff:main.tf
-  instance_type = "t2.micro"
+  instance_type = "t2.small"
```

反映内容を確認する

```bash
% terraform plan
aws_instance.tutorial_instance: Refreshing state... [id=i-xxxxxxxxxxxxxxxxx]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # aws_instance.tutorial_instance will be updated in-place
  ~ resource "aws_instance" "tutorial_instance" {
        id                                   = "i-xxxxxxxxxxxxxxxxx"
      ~ instance_type                        = "t2.micro" -> "t2.small"
        tags                                 = {
            "Name" = "TutorialInstance"
        }
        # (29 unchanged attributes hidden)

        # (7 unchanged blocks hidden)
    }

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

`yes` を入力して反映する

```bash
% terraform apply

.
.
.

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.tutorial_instance: Modifying... [id=i-xxxxxxxxxxxxxxxxx]
aws_instance.tutorial_instance: Still modifying... [id=i-xxxxxxxxxxxxxxxxx, 10s elapsed]
aws_instance.tutorial_instance: Still modifying... [id=i-xxxxxxxxxxxxxxxxx, 20s elapsed]
aws_instance.tutorial_instance: Still modifying... [id=i-xxxxxxxxxxxxxxxxx, 30s elapsed]
aws_instance.tutorial_instance: Still modifying... [id=i-xxxxxxxxxxxxxxxxx, 40s elapsed]
aws_instance.tutorial_instance: Still modifying... [id=i-xxxxxxxxxxxxxxxxx, 50s elapsed]
aws_instance.tutorial_instance: Still modifying... [id=i-xxxxxxxxxxxxxxxxx, 1m0s elapsed]
aws_instance.tutorial_instance: Modifications complete after 1m3s [id=i-xxxxxxxxxxxxxxxxx]

Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
```

マネコンからも更新されたのが確認できる

![スクリーンショット 2022-12-17 17.23.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213486/56932c46-c19c-1522-e063-ee9003e8011f.png)


試したのはインスタンスタイプの変更だったため何事もなく変更が行われた
例えばamiを変更するなど、**変更するものによってはreplacement（既存リソース削除+新規作成）が発生する**ので、planで内容をしっかり確認するのが大事

## 破棄

反映内容を確認する

```bash
% terraform plan -destroy
aws_instance.tutorial_instance: Refreshing state... [id=i-xxxxxxxxxxxxxxxxx]

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.tutorial_instance will be destroyed
  - resource "aws_instance" "tutorial_instance" {
          .
          .
          .
    }

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

`yes` を入力して反映する

```bash
% terraform 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.tutorial_instance: Destroying... [id=i-xxxxxxxxxxxxxxxxx]
aws_instance.tutorial_instance: Still destroying... [id=i-xxxxxxxxxxxxxxxxx, 10s elapsed]
aws_instance.tutorial_instance: Still destroying... [id=i-xxxxxxxxxxxxxxxxx, 20s elapsed]
aws_instance.tutorial_instance: Still destroying... [id=i-xxxxxxxxxxxxxxxxx, 30s elapsed]
aws_instance.tutorial_instance: Still destroying... [id=i-xxxxxxxxxxxxxxxxx, 40s elapsed]
aws_instance.tutorial_instance: Still destroying... [id=i-xxxxxxxxxxxxxxxxx, 50s elapsed]
aws_instance.tutorial_instance: Destruction complete after 51s

Destroy complete! Resources: 1 destroyed.
```

マネコンからも更新されたのが確認できる

![スクリーンショット 2022-12-17 17.34.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213486/d95bcd16-222f-3dd6-fa92-274c67c510a9.png)


# 完全に理解した

これが基本的な内容で、この後すでに見えてるのが以下

- stateファイルは大体クラウドで管理するっぽい
    - 最新の状態をみんなで参照できないといけないから
    - awsならs3で、とか
- module化とかインポートして利用ができるようで、宗派によっていろんなおすすめフォルダ構成があるっぽい
    - moduleってのが今後便利に使うには必ずおさえないといけない概念っぽい
    - providerブロックやresourceブロックを別ファイルにまとめたり、とか
- グローバル変数定義ファイルとか用意していい感じにしたりするっぽい
    - hoge.tfvarsとかvariables.tfというものがあるっぽい

完全に理解できたので、ﾁｮｯﾄﾃﾞｷﾙを目指してまずは上記のあたりを次アクションとして考えておく