【初心者向け】細かいこと抜きでとりあえずTerraformを触ってみる

# はじめに
この記事では、「とりあえず細かいことは考えずに、Terraformを試してみる」というテーマで、Terraformを使用してAWSのリソースを作成・変更・削除してみることを目標にします。

記事の構成は以下の通りです。

1. 実行準備をしてみる
2. Terraformの記法をゆるく眺めてみる
3. 実際にtfファイルを作成してみる
4. 実際に実行してみる～リソース作成～
5. 実際に実行してみる～リソース変更・削除～

細かいことは抜きたいので、以下の要素等はまとめない予定です。
- インフラストラクチャのコード（IaC）とは？
- Terraformとは何か？
- Terraformのメリットとデメリット
- providerとは？
- tfstateとは？
- moduleとは？
- リソースの設計やtfstateの管理・運用のベストプラクティスとは？
- などなど・・・

ベストプラクティス等を含め体系的に学びたい初心者の方は、以下の技術書がオススメです。

https://www.amazon.co.jp/dp/B07XT7LJLC

qiita内に関連する記事が存在するのでこちらも併せて学ぶと良いと思います。

https://qiita.com/ayatothos/items/27024e8168a8b766bcd3

https://qiita.com/obr-note/items/0278ca3be1c81f262ad7

### 1.実行準備をしてみる
###### IAMユーザー
Terraformを使用してAWSのリソースを作成するためには、実行用のユーザー情報が必要になります。
セキュリティベストプラクティス的にはタブーなのですが、今回はAdministratorAccessポリシーをアタッチしたユーザーを用意しました。
Terraformなど、コンソール以外からこのIAMユーザーを使用する場合は「アクセスキー」が必要になるので発行します。

###### AWS CLI
AWS CLIも別途インストールしておきます。
https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/getting-started-install.html
```
$ aws --version
aws-cli/2.5.2 Python/3.9.11 Linux/5.4.72-microsoft-standard-WSL2 exe/x86_64.ubuntu.20 prompt/off
```

###### Terraform
Terraformもインストールします。
Terraformにはtfenvというバージョン管理ツールが存在します。nodeでいうnvmです。
直接Terraformをインストールするより、tfenv経由でインストールする方が便利です。
https://github.com/tfutils/tfenv
```
$ tfenv --version
tfenv 3.0.0-18-g1ccfddb
$ terraform --version
Terraform v1.5.4
```

###### 環境変数
最後に、環境変数として用意したIAMユーザーのACCESS_KEYとSECRET_ACCESS_KEYを設定します。
```
export AWS_ACCESS_KEY_ID=XXXXXXXXXXXXXXXXXXXXX
export AWS_SECRET_ACCESS_KEY=XXXXXXXXXXXXXXXXXXXXX
```

### 2.Terraformの記法をゆるく眺めてみる
Terraformはディレクトリ内にある「～.tf」ファイルを参照し、インフラリソースなどを作成するツールです。
.tfファイルには、インフラリソースの設定のみならず、変数の設定や、terraformのバージョン指定設定などを記載することができます。
この各設定は「ブロック」という単位で分けられます。8種類あるブロックをゆるくまとめようと思います。

###### terraformブロック
Terraformプロジェクト全体の定義を行います。Terraformの要求バージョンや、インフラリソースの状態を管理するファイル（tfstate）の管理場所などを設定できます。

###### providerブロック
各クラウドサービスの個別設定を行います。AWSであればAWS CLIのプロファイルや、リージョンなどを設定します。

###### resourceブロック
各クラウドサービスのリソースの設定を行います。
インフラリソースを作成する際に設定し、リソースごとに様々な要素を設定します。
※厳密にはクラウドサービスのリソース以外にも使用するのですが、まずはこの理解で大丈夫です。

###### dataブロック
Terraformプロジェクトの外部で定義された情報を参照する場合に設定します。
Terraformプロジェクトの外部で定義された情報とは、AWSが定義しているマネージドなリソース（マネージドポリシーやami、マネージドルールグループ）や、別のTerraformプロジェクトなどで作成した既に存在するインフラリソースの情報を指します。

###### localsブロック
tfファイル内で参照可能な変数を設定できます。

###### variableブロック
tfファイル内で参照可能な変数を設定できます。
localsブロックと異なり、Terraformの実行時に、引数や別ファイルから値を引き渡すことができます。

###### moduleブロック
Terraformは「モジュール」という単位でインフラリソース設定をテンプレート化できます。
モジュール化したtfファイルを参照する場合に設定します。

###### outputブロック
モジュール化したtfファイル内で設定された値を、モジュール外のtfファイルで参照できるようにするために設定されます。

###### ブロックの役割まとめ
①terraformブロック、providerブロックでTerraformとAWS（などのクラウドプロバイダー）の設定を行う

②dataブロック、localsブロック、variableブロックで外部データや変数などリソースの作成に必要な値を設定する

③resourceブロックで実際にインフラリソースを作成する

④moduleブロックやoutputブロックを活用し、インフラのテンプレート化を行い、再利用性や利便性を高める

以上のようなイメージを持てればまずは大丈夫だと思います。

### 3.実際にtfファイルを作成してみる

###### tfファイル作成
早速tfファイルを作成してみます。下記コードをコピペすればOKです。
```main.tf
# terraformブロック
# Terraformプロジェクト全体の定義を行います。
terraform {
    # Terraformのバージョン指定
    required_version = "~> 1.5.0"

    # Terraformのaws用ライブラリのバージョン指定
    required_providers {
        aws = {
            source  = "hashicorp/aws"
            version = "~> 5.31.0"
        }
    }
}

# providerブロック
# 各クラウドサービスの個別設定を行います。
provider "aws" {
    # リージョンを指定
    region = "ap-northeast-1"
}

# localsブロック
# tfファイル内で参照可能な変数を設定できます。
locals {
    # key-value形式で自由に設定できます。
    local_example = "local_example_value"
    local_example_num = 1
    local_example_boolean = true
}

# variableブロック
# localsブロックと異なり、Terraformの実行時に、引数や別ファイルから値を引き渡すことができます。
variable "example_variable" {
    # 型を指定することで、値を引き渡した場合の型チェックが行えます
    type    = string

    # デフォルト値を設定することも可能です
    default = "default_value"
}

# resourceブロック
# 各クラウドサービスのリソースの設定を行います。
# インフラリソースを作成する際に設定し、リソースごとに様々な要素を設定します。
# ↓ブロックの種類、リソースの種類、ブロック名の順で定義します。
resource "aws_s3_bucket" "example" {
    # バケット名を設定します。ユニークな値ではない場合、エラーが発生します。
    bucket = "example-s3-bucket-w6yv1i9ds0"

    # ほとんどのawsリソースにはタグが設定できます。
    tags = {
        # localsブロックとvariableブロックから値を取得してタグ付けしてみます。
        Local    = "local_example is ${local.local_example}"
        Variable =  "example_variable is ${var.example_variable}"
    }
}

# resourceブロック
# インフラリソースのほとんどの設定はメインとなるリソース定義（S3であれば「aws_s3_bucket」、EC2であれば「aws_instance」）内で設定できますが、
# 一部設定は別リソースとして設定することが推奨されています（S3であればバケットのバージョニング設定やライフサイクル設定、EC2であればインスタンスの実行状態の変更設定など）。
resource "aws_s3_bucket_versioning" "versioning_example" {
    # ${リソースの種類}.${ブロック名}.${リソースのプロパティ}で別ブロックの値を参照できます。
    bucket = aws_s3_bucket.example.id

    # バージョニングを有効化
    versioning_configuration {
        status = "Enabled"
    }
}

# dataブロック、moduleブロック、outputブロックは今回使用対象外とします。
# data "example" {}
# module "example" {}
# output "example" {}

```
業務の100倍コメントを残していますので詳細は省きますが、S3バケットを1つ作り、バージョニングを有効化する設定を施しています。

###### tfファイル動作確認
Terraformには実際にリソースを作成せず、作成予定だけを確認するコマンドがあるので、そのコマンドを使用してみます。

まずは「terraform init」コマンドで必要ライブラリをインストールします。npm installのイメージです。
```
terraform init
```
![sc1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3654730/828ef2c7-53be-4653-17af-8e2c290464fe.png)

main.tfの11行目で設定したverのaws用ライブラリをインストールしていることが確認できます。

では実際に「terraform plan」コマンドでリソースの作成予定を確認してみます。
```
terraform plan
```
![sc2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3654730/433372a4-4a34-1ba2-13a0-5df5d8e6c200.png)

ずらーっとログが流れますが、今回は細かいこと抜きなので、最後の部分だけ確認すれば大丈夫です。
>Plan: 2 to add, 0 to change, 0 to destroy.

リソースの作成予定が確認できます。わかりづらいですが、今回は「S3バケット本体」「バケットのバージョニング設定変更」の2つをリソースとしていますので、2 to addと表示されることが分かります。
>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.

planコマンドはあくまで作成予定確認コマンドで、リソース作成はまだであることをお知らせしてくれています。
問題がなさそうなので、「terraform apply」コマンドでリソースを作成してみます。

### 4.実際に実行してみる～リソース作成～
「terraform apply」コマンドでリソースを作成します。
```
terraform apply
```
![sc3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3654730/8a560b28-326f-7a45-68d7-df89af54e3ce.png)

plan時と同様のログが流れ、実行許可を要求されます。
>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」を入力し、リソース作成を実行します。

![sc4.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3654730/4065567c-6311-2f8b-a42e-60fda505c3d5.png)


ログが再び流れ、リソース作成が完了しました！

実際にマネジメントコンソールからS3の状態を確認します。
バケットの作成や、タグ付け、バージョニングの有効化がうまくいっていることが確認できます。

![sc5.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3654730/85d4fff9-bcd6-285b-6be3-907624a3c815.png)
![sc6.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3654730/48880470-fe98-4a42-0402-954493ceafa7.png)

### 5.実際に実行してみる～リソース変更・削除～

###### リソースの状況確認

簡単にですが、リソースの設定を変更してみます。
まずは現在の状態を確認するために、再度「terraform plan」を実行します。
```
terraform plan
```
![sc7.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3654730/6c6aabc5-6526-3d41-dedd-fc4401e15af7.png)

>No changes. Your infrastructure matches the configuration.

```No changes.```が表示されました。
tfファイルの設定内容と、現在のリソースの設定内容に乖離がないことがわかります。

###### リソースの設定内容変更
では、実際にリソースの設定内容を変更し、Terraformの挙動を確認します。

以下のコマンドを実行します。variableブロックで定義している${example_variable}の値を実行時引数で上書きします。
```
terraform plan -var="example_variable=overwriting_value"
```
![sc8.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3654730/f879937a-54f5-94ab-2bc4-7b15c5c2163b.png)

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

```1 to change```が表示されました。

${example_variable}はS3バケットのタグ設定に使用しているので、値が変更された影響で、リソースの設定が変更されることをTerraformが教えてくれています。

再度applyを使用して、リソースの設定が変更できることを確認してみます。
```
terraform apply -var="example_variable=overwriting_value"
```

![sc9.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3654730/799a58ca-e260-baf7-3b8a-a967fde3f0c0.png)

```1 changed``` が表示されました。

![sc10.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3654730/92613d95-f20e-e030-f218-4ea5b46e5ed2.png)

タグも変更されていることが確認できます。

最後に、「terraform destroy」コマンドでリソースの削除を行ってみます。
```
Plan: 0 to add, 0 to change, 2 to destroy.
```
![sc11.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3654730/c3ff3c13-c8d9-6cb0-579c-6e700ef4d77e.png)

```2 to destroy.``` が表示されました。
「S3バケット本体」「バケットのバージョニング設定変更」の2リソースが削除されます。

applyと同様、yesを入力します。

![sc12.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3654730/74de7638-6f67-91f3-61e7-fb75e3175e97.png)

>Destroy complete! Resources: 2 destroyed.

削除が成功されました。

バケットが存在しないことをマネジメントコンソール上で確認します。

![sc13.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3654730/dd22d2f9-23bf-ce94-7e53-7534970bf0d3.png)

以上で、Terraformでのインフラリソースの作成・変更・削除を一通り体験できました。

## 最後に
今回はサクッとTerraformの使い方の初歩の初歩をまとめてみました。
各ブロックの役割や、init、plan、apply、destroyの流れがなんとなく理解できれば良いかと思います。

#### おまけ
記事冒頭で掲載した技術書と関連qiita記事を再掲します。おすすめです。
ベストプラクティス等を含め体系的に学びたい初心者の方は、以下の技術書がオススメです。

https://www.amazon.co.jp/dp/B07XT7LJLC

qiita内に関連する記事が存在するのでこちらも併せて学ぶと良いと思います。

https://qiita.com/ayatothos/items/27024e8168a8b766bcd3

https://qiita.com/obr-note/items/0278ca3be1c81f262ad7

#### 感想
S3バケットを1つ作っただけなのであまり達成感がないですね・・・。
また、IaCの神髄を発揮するには、今回触らなかったモジュール化などのテクニックが必須だと思います。
ということで、今回使わなかったdataブロック、moduleブロック、outputブロックを使用したサンプルを使う記事も書けたらなーと思います。