Terraform講座：環境構築・フォルダ構成編

## 今回の記事について
今回は、Terraform講座の第三回として、Terraformの環境構築やフォルダ構成について解説します！！
前回の記事は[こちら](https://qiita.com/aaaaa_326/items/b32621f4cbaa1434d925)からどうぞ！

## 環境構築
環境構築は様々ありますが、ここでは、tfenvでの構築方法について説明します！
tfenvは、Terraformのバージョンを簡単に管理することが出来るツールです。詳しくは[こちら](https://github.com/tfutils/tfenv)をご覧ください！

### 環境構築の手順
**1. tfenv自体のインストール**

1-1. tfenvのgithubリポジトリをクローンする。
```
$ git clone --depth=1 https://github.com/tfutils/tfenv.git
```
1-2. `bin`について`$PATH`に追加する。
```
# bashの場合
$ echo 'export PATH="./tfenv/bin:$PATH"' >> ~/.bash_profile

# zshの場合
$ echo 'export PATH="./tfenv/bin:$PATH"' >> ~/.zprofile
```
※ macユーザの場合は以下のコマンドだけでインストール出来ます！
```
$ brew install tfenv
```

**2. tfenvによるTerraformのインストール**

2-1. 利用可能なTerraformのバージョンを列挙して、自分が使うバージョンを探す。
```
$ tfenv list-remote
```

2-2. インストールする。ここで書いた `1.10.5` はあくまで例です！
```
$ tfenv install 1.10.5
```

2-3. インストール後、使用するバージョンを指定する
```
$ tfenv use 1.10.5
```

**3. インストールされたか確認！！**
```
$ terraform --version
Terraform v1.10.5
```
このように、Terraformのバージョンが出力されたら環境構築完了です！

## フォルダ構成
環境が整ったところで、次は、Terraformによるインフラ構築プロジェクトを作っていきましょう。
複数人での構築を行う際は、このような構成にすると開発がよりスムーズになります！
```
root
  ├─ backend.tf
  ├─ main.tf
  ├─ provider.tf
  ├─ .terraform-version
  ├─ version.tf
  ├─ modulesA
  │    ├─ variables.tf
  │    ├─ outputs.tf  
  │    └─ xxxxx.tf
  ├─ modulesB
  │    ├─ variables.tf 
  │    ├─ outputs.tf 
  │    └─ xxxxx.tf
  ├─ .../  
```

まず、root直下にある`xxxxx.tf`ファイルですが、開発・検証・商用環境のそれぞれでインフラを構築する場合は、以下のようにするのがおすすめです！
```
root
  ├─ environments
  │    ├─ dev
  │    │   ├─ backend.tf
  │    │   ├─ main.tf
  │    │   ├─ provider.tf
  │    │   └─ version.tf
  │    ├─ stg ── .../  
  │    └─ prd ── .../
  ├─ .terraform-version
```

次に、`moduleX` ディレクトリですが、ここには、リソース構築のコードを置きます。例えば、`ec2.tf` や `vpc.tf` といったものです。これらのファイルの管理する単位は自由です。
先ほどのようにリソース単位で区切ったり、用途別に`ec2_xxxx.tf` のように管理しても最終的に構築されるリソースは同じになります。そのからくりについて、各ファイルの役割を踏まえて説明します。


### ファイルの役割
ここでは、ファイルの役割について大まかに説明します。詳細部分 (一部の各ファイルの例文) は、次回以降に解説したいと思います！

**`main.tf`**
Terraformのプロジェクトにおいて、最も重要なファイルです。Terraformは、`main.tf` に書かれているリソースについて構築します。
そのため、結論から言うと、`main.tf` だけ作成し、その中で全ての内容を書いてしまえば、Terraformを実行することが出来ます。
しかし、それでは長文になり、設定が混在することで可読性が悪く、複数環境でリソースを構築したい場合にコードを再度書く必要があるなど、利便性も悪いです。そこで、上記のような構成をとると、機能ごとに設定を記述できるので可読性が上がったり、モジュール化することで、複数環境での`main.tf` において書くコード量が減るので、利便性も向上します！

**`backend.tf`**
[前回](https://qiita.com/aaaaa_326/items/b32621f4cbaa1434d925)の記事で、Terraformは宣言型の言語で、状態を把握しているファイルがあると解説しました。
`backend.tf`は、その状態把握のファイル＝stateファイルをどこに保存するかを指定します。
指定がない場合、stateファイルは開発者のローカル環境に作成されます。その場合、複数人で開発を行うと、ローカル環境にいくつもstateファイルが存在することになり状態がバラバラでどれが正しいのかが分からなくなるリスクがあります。
そこで、`backend.tf` で一律のstateファイルを作成・保存することで上記のリスクを無くすことが出来ます。
大体の場合は、AWSのS3で保存する場合が多いです。

**`provider.tf`**
Terraformプロジェクトで使用するクラウドサービス等を定義する役割を持ちます。
クラウドサービスの定義に加え、リージョンの指定、`alias` による直感的な使い分けを行うことが出来ます。つまり、複数リージョンに跨るシステムのインフラを1つのプロジェクトで開発することが可能です。
```terraform:provider.tf
terraform {
    required_providers {
        aws = {
            source = "hashicorp/aws"
            version = "~> 5.40.0"
            configuration_aliases = [aws.tokyo, aws.osaka]
        }
    }
}

provider "aws" {
    region = "ap-northeast-1"
    alias = "tokyo"
}

provider "aws" {
    region = "ap-northeast-3"
    alias = "osaka"
}
```

**`version.tf` & `.terraform-version`**
文字通りですが、Terraformのバージョンを制限し、複数人での開発におけるバージョン差異を無くす役割を果たしてくれます！
まず、`.terraform-version` は、`tfenv` などのバージョン管理ツールが参照するファイルであり、ここに記述されているTerraformのバージョンを自動的にインストールし、切り替えまで行ってくれます。
```.terraform-version
1.10.5
```
次に、`version.tf` は、プロジェクト内において使用するバージョンについて定義するファイルです。機能するところとしては、Terraformを実行する際で、指定されたバージョンがインストールされているか検証し、バージョンが一致していない場合はエラーを発生させます。2つを組み合わせると、`version.tf` で指定されている条件を満たすバージョンが自動的に全てのユーザにインストールされることとなり、バージョン差異が無い開発環境を整えることが出来ます！
```terraform:version.tf
terraform {
    required_version = "1.10.5"
}
```

**`variables.tf` & `outputs.tf`**
これらは、変数の設定ファイルであり、`variables.tf` は、基本的にモジュールにおいて引数となる部分を宣言する際に使用します。個人的に、Terraformにおける変数の説明は以下の記事が非常に分かりやすかったので載せておきます！

https://qiita.com/hajimeni/items/4afcac38e4f275edb852

また、`output.tf` は、あるファイルで作成したリソースの一部を別のファイルで使用したい際にどのような形で出力するかを定義するファイルになっており、両者ともファイル間の値の受け渡しの役割を担っています。

## インフラ構築
ここまで出来ればあとは、リソースのコードを書いて、自身のAWSアカウントなどにリソースを構築するだけです！
### リソース構築の手順
**1. `main.tf` が存在するディレクトリまで移動する。**
以下は、先ほどのフォルダ構成の場合の移動方法になります。
```
$ cd ./root/environments/dev
```
**2. プロファイル設定を行う**
これを行わなければ、所望のアカウントにインフラを構築できません！ここではAWSでの設定方法を載せておきます！

2-1. AWS CLIのインストール (macの場合)
```
$ brew install awscli
```

2-2. AWSのマネジメントコンソールで、アクセスキーを作成し、IDとシークレットアクセスキーを確認する
※ シークレットアクセスキーについては作成時しか確認できないので要注意！

2-3. 自身のプロファイルを設定
この時、`--profile` を指定することで、名前付きのプロファイルを設定できます！
そして、2-2で確認したアクセスキーIDとシークレットアクセスキーを入力します。
```
$ aws configure --profile xxxxx
AWS Access Key ID [None]: ********************
AWS Secret Access Key [None]: ********************
Default region name [None]: ap-northeast-1
Default output format [None]:
```
最後に、`AWS_PROFILE` にプロファイルを設定して完了です。

```
$ export AWS_PROFILE=xxxxx

# プロファイルの確認
$ aws configure list
```

**3. プロジェクトの初期化**
`.tf` ファイルを解析し、最適なダウンロード処理を行います。
```
$ terraform init
```

**4. いざリソース構築！**

4-1. planを行い、実際に構築されるリソースについて確認する。
※ チーム開発の場合、想定外のリソースが消去されたり生成されるので、手順4-2を行う前に必ずこの手順を踏みましょう！
```
$ terraform plan
```

4-2. 問題がないことを確認したうえで、applyを行い、リソース構築！
```
$ terraform apply
```

最後に実際にアカウントにログインしてリソースが構築出来ているか確認しましょう！
お疲れさまでした！！


## まとめ
- Terraformの実行環境：`tfenv` でサクサク構築しよう！
- 開発のフォルダ構成：最初にフォルダ構成と実装環境は固めよう！(全てのプログラムに言えますが笑)
- リソース構築：planを行ってからapply！

長い記事になっちゃいましたが、お読みいただきありがとうございます！
[次回](https://qiita.com/aaaaa_326/items/a82ee52b6d2dd7274c6e)は、ハンズオン的にAWSリソースを構築するコードを書いていきます！！！