改めてTerraformの勉強をし直してみた（Cloud基盤編）

# なんとなくTerraform触ってきたけど
IT企業に入社して右も左もわからず、いきなりTerraformで環境構築しろと言われたあの日。
人の真似をしたり聞いたり、調べたりで何とかこれまでやってきたけど、改めて振り返って、点と点を結んで線にしてみよう！ということで、Terraformとはなんぞやを書き書きしてみる。

# Terraformとは
TerraformはInfrastructure as Code (IaC)と呼ばれる考え方を実現するツールの一種で、インフラの構成をソースコードとして記載し管理できる。
インフラのプロビジョニング、変更、削除など、Terraformではコードにより宣言し、実行することで記載したインフラ構成が構築できる。

イメージはこんなん。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3395028/c419b0e2-350f-2642-620f-d5bf75dabfdb.png)

# IaCとは
Infrastructure as Code (IaC) とは、GUIやCLI、手動でインフラを構築する代わりに、コードを使用してインフラの管理とプロビジョニングを行う考え方のこと。インフラ構成や設定値をソースコードとして記載するため、手動設定による手間を軽減し、エラーの発生を低減できる。その他、インフラ構成のバージョン管理を可能にする。


# IaCって何が嬉しいの？
もしかしたら他にあるかもですが、個人的にはこうかと。
1. インフラ構成の理解がしやすい
2. インフラ構成の再現が容易である
3. インフラ構成変更に強い

コードを見れば利用しているリソースやパラメータが一目でわかる上に、リソースの追加やパラメータの設定変更は、コードを書き直し実行するだけで構成が環境に反映される。新しく別な環境を作るにしても、ソースコードを複製すれば同様の構成も再現可能となる。

なんと手軽で美しいことか。

# Terraformのブロックタイプ
Terraformは基本的にブロックでコードを記載する。
タイプは以下。
| 種類          |  説明                                              |
|:------------:|:--------------------------------------------------:|
| terraform    | terraformの定義                                    |  
| provider     | プロバイダの定義                                    |
| module       | 再利用可能なTerraformコードの単位                    |
| resource     | terraform管理対象となるリソースの定義                | 
| variables    | 外部から変更できる変数を定義                         |
| data         | terraform管理していないリソースを参照したい場合に定義 |
| output       | 外部から参照したい値を定義                           |
| locals       | 外部から変更できない変数を定義                       |

# Terraformサンプルコード

### アウトプット
- Cloud Run 1サービス
- ServiceAccount 1アカウント
- Service AccountへのSQL管理者のロール付与
- Cloud Run サービスへのService Accountのアタッチ

### 前提
- Terraformやgcloud CLI、Dockerなどのもろもろの環境を構築済み
- CloudRunへのデプロイイメージが作成済み
- GCP Projectを作成済み

### ファイル構成
```
sample-iac/
　├ develop/
　│　├ main.tf
　│　├ provider.tf
　│　├ values.tf
　│　└ version.tf
　└ modules/
　 　├ cloudrun
　 　│　├ main.tf
　 　│　├ variables.tf
　 　└ sa
　 　 　├ data.tf
　 　 　├ main.tf
　 　 　├ output.tf
　 　 　└ variables.tf
```

### 1. terraform
Terraformのバージョン,プロバイダの指定,ステートファイルの格納場所等を記述する。
```tf:version.tf
terraform {
  required_version = "0.13.1"
    required_providers {
      goole = {
        source  = "hashicorp/aws"
        version = "~> 5.54.1"
      }
    }
    backend gcs {
        bucket = "Bucket Name" #作成したBucke名を入れる。
    }
}
```

### 2. provider
指定したプロバイダを利用するために、リージョンや認証情報の設定等を行う。
```tf:provider.tf
provider google-beta {
    project = local.project
    region  = local.region
}
```

### 3. module
今回はCloudRun関連とServiceAccount関連でモジュール化する。
source でmoduleの中身を呼び出し。
```tf:main.tf
module cloudrun {
    source               = "../modules/cloudrun"
    project              = local.project
    region               = local.region
    cloudrun             = local.cloudrun
    service_account_name = module.sa.sa.email
}
module sa {
    source               = "../modules/sa"
    project              = local.project
    sa                   = local.sa 
}
```

### 4. locals
各リソースを作成する際に利用する値を定義する。
以下で指定した値をmoduleで呼び出している。
```tf:values.tf
locals {
    project  = "regret-email-proj"
    region   = "asia-northeast1"
    cloudrun = {
        name  = "sample-cloudrun"
        image = "asia.gcr.io/regret-email-proj/cloudrun/hello:latest"
    }
    sa = {
        account_id = "sample-cloudrun-sa"
        display_name = "Sample Service Account"
    }
}
```

### 5. resource
Terraform で管理するリソースを定義する。
設定した内容に基づいて、実際のリソースが構築される。
```tf:modules/cloudrun/main.tf
resource "google_cloud_run_service" "sample" {
  project     = var.project
  location    = var.region
  name        = var.cloudrun.name
  template {
    spec {
        service_account_name = var.service_account_name
        containers {
          image = var.cloudrun.image
        }
    }
  }
}
```

```tf:modules/sa/main.tf
resource "google_service_account" "sample" {
  project      = var.project
  account_id   = var.sa.account_id
  display_name = var.sa.display_name
}

resource "google_project_iam_member" "sa" {
  project = var.project
  member  = google_service_account.sample.member
  role    = data.google_iam_role.sample_role.name
}
```

### 6. variables
variable宣言で各変数を定義することによって、var.変数名でresourceで値を参照可能にする。
```tf:modules/cloudrun/variables.tf
variable project {}
variable region {}
variable cloudrun {}
variable service_account_name {}
```

```tf:modules/sa/variables.tf
variable project {}
variable sa {}
```

### 7. data
既存のインフラリソースのデータを参照・読み取ることができる。他のリソースの情報や設定を取得し、外部や管理外のリソース情報をTerraform内で利用できる。
今回はGCPで定義されている「CloudSQL管理者」ロールをデータとして取得し、Terraform内で利用可能にしている。
```tf:modules/sa/data.tf
data "google_iam_role" "sample_role" {
    name = "roles/cloudsql.admin"
}
```

### 8. output
Terraformの実行結果を出力し、他のModuleで利用可能にしたり、実行結果として出力することができる。
```tf:modules/sa/output.tf
output "sa" {
    value = google_service_account.sample
}
```

## Terraform コマンド
コマンドは記事がいくらもであるので、主要な部分だけ。
```terminal:Terminal
# 構成プロファイルを初期化し必要なプラグインやモジュールをインストールしてくれる。
terraform init

# 変更の差分確認が可能で、差分を出力してくれる。
terraform plan

# 変更を環境に適応する。
terraform apply
```
この他、tfenvをインストールしておくと楽かも。