Terraformで複数CodeBuildの複数環境変数を管理したい

# はじめに
[CYBIRD Advent Calendar 2024](https://qiita.com/advent-calendar/2024/cybird)の14日目担当の[もっふんにゃ](@moffunnya)です。
13日目は@cy-naullさんの「ClaudeAIたちってどんな感じなの？」でした。

14日目のこの記事では、Terraform で行う CodeBuild の環境変数についてまとめてみました。

# 経緯
Terraform で Apply した CodeBuild プロジェクト内に環境変数を追加することになり、先んじてAWSコンソールから環境変数を追加したのですが、Terraform との差分を修正する際に四苦八苦した為、備忘録として残すことにしました。

CodeBuild の環境変数については、以下クラスメソッド様の記事が分かりやすいのでご参照ください。

https://dev.classmethod.jp/articles/codebuild-env/

# 既存の状態
terraform init や terraform plan を実行するディレクトリで、
以下 codebuild.tf を作成してこの中で CodeBuild を作成しています。

```diff_terraform
codebuild.tf

resource "aws_codebuild_project" "this" {
  for_each = local.codebuild_project
  
  name          = each.value.name
  description   = each.value.description
  build_timeout = each.value.build_timeout
  service_role  = 良しなに

  artifacts {
    type = "NO_ARTIFACTS"
  }

  environment {
    compute_type                = each.value.build_compute_type
    image                       = each.value.build_image
    type                        = "LINUX_CONTAINER"
    image_pull_credentials_type = "CODEBUILD"
  }

  ～省略～
  
}
```

codebuild.tf の for_each から var.codebuild.tf の codebuild_project 内に羅列したキーを参照し、繰り返し作成しています。

```diff_terraform
var.codebuild.tf

codebuild_project = {
    project_a = {
      name               = "BUILD_PROJECT_A"
      description        = "BUILD_PROJECT_Aのせつめー"
      buildspec          = "BUILD_PROJECT_Aで使うymlファイル"
      source_location    = "https://github.com/使うリポジトリ"
      build_image        = "aws/codebuild/使うビルドイメージ"
      build_compute_type = "BUILD_GENERAL1_SMALL"
      build_timeout      = いい感じの数字
    }

    project_b = {
      name               = "BUILD_PROJECT_B"
      description        = "BUILD_PROJECT_Bのせつめー"
      buildspec          = "BUILD_PROJECT_Bで使うymlファイル"
      source_location    = "https://github.com/使うリポジトリ"
      build_image        = "aws/codebuild/使うビルドイメージ"
      build_compute_type = "BUILD_GENERAL1_SMALL"
      build_timeout      = いい感じの数字

    project_c = {
      name               = "BUILD_PROJECT_C"
      description        = "BUILD_PROJECT_Cのせつめー"
      buildspec          = "BUILD_PROJECT_Cで使うymlファイル"
      source_location    = "https://github.com/使うリポジトリ"
      build_image        = "aws/codebuild/使うビルドイメージ"
      build_compute_type = "BUILD_GENERAL1_SMALL"
      build_timeout      = いい感じの数字
    }
}
```

# 環境変数を追加する場所
https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/codebuild_project

公式の aws_codebuild_project リソースのドキュメントから、環境変数を enviroment_variable で管理することが分かりました。

```diff_terraform
ドキュメントから抜粋

  environment {
    compute_type                = "BUILD_GENERAL1_SMALL"
    image                       = "aws/codebuild/amazonlinux2-x86_64-standard:4.0"
    type                        = "LINUX_CONTAINER"
    image_pull_credentials_type = "CODEBUILD"

    environment_variable {
      name  = "SOME_KEY1"
      value = "SOME_VALUE1"
    }

    environment_variable {
      name  = "SOME_KEY2"
      value = "SOME_VALUE2"
      type  = "PARAMETER_STORE"
    }
  }
```

こう記述することは分かったのですが、それぞれのビルドプロジェクトに別の環境変数を登録する必要があり...。
ドキュメントに記載の形だとそれぞれのビルドプロジェクトに同じ環境変数が登録されてしまう為、試行錯誤しました。

# いざ環境変数追加
ドキュメントどおり aws_codebuild_project リソースがある codebuild.tf 内に環境変数を追加していきました。

```diff_terraform
codebuild.tf

//①
locals { 
  pj_a = [
    {
      name  = "shittori"
      value = "しっとり"
      type  = "PLAINTEXT"
    },
    {
      name  = "mossari"
      value = "もっさり"
      type  = "PLAINTEXT"
    }
　]
  pj_b = [
    {
      name  = "heyhey"
      value = "ヘイヘイ"
      type  = "PLAINTEXT"
    },
    {
      name  = "runrun"
      value = "ルンルン"
      type  = "PLAINTEXT"
    }
  ]
　other = [
    {
      name  = "empty"
      value = "empty"
      type  = "PLAINTEXT"
    }
  ]

}

resource "aws_codebuild_project" "this" {
  for_each = local.codebuild_project
  
  name          = each.value.name
  description   = each.value.description
  build_timeout = each.value.build_timeout
  service_role  = 良しなに

  artifacts {
    type = "NO_ARTIFACTS"
  }

  environment {
    compute_type                = each.value.build_compute_type
    image                       = each.value.build_image
    type                        = "LINUX_CONTAINER"
    image_pull_credentials_type = "CODEBUILD"

    //②
    dynamic "environment_variable" {
      //③
      for_each = var.name == "BUILD_PROJECT_A" ? {
        for i in local.pj_a : i.name => i 
      } : var.name == "BUILD_PROJECT_B" ? { 
        for i in local.pj_b : i.name => i 
      } : {
        for i in local.other : i.name => i 
      }

      content {
        name                = environment_variable.value.name
        value               = environment_variable.value.value
        type                = environment_variable.value.type
      }
    }
  }

  ～省略～
  
}
```
### 説明
① locals でビルドプロジェクトAとBそれぞれに登録する環境変数を pj_a, pj_b として記述し、別で other を記述します。

②環境変数が複数ある為、dynamic と for_each を用いて繰り返し environment_variable 作成しています。

③ for_each 内で var.codebuild.tf の name に該当する環境変数を三項演算子で識別しています。

locals で other を作成している理由としては、三項演算子の最後の else 部分に同じ型の値を入れないと、plan でエラーになるため、利用しない環境変数（empty）を設定し参照しています。

これらを Apply すると以下のようにビルドプロジェクトが作成されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2983941/0db546f5-d7bd-41dd-a5d7-129d653cab10.png)

BUILD_PROJECT_A の環境変数
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2983941/c2feea42-7b40-d8b2-e73d-a5b823d41eb3.png)

BUILD_PROJECT_B の環境変数
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2983941/d94596ec-b278-0dcb-25fd-e5e5f7665127.png)

BUILD_PROJECT_C の環境変数
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2983941/52a04236-d1e2-24cc-0df2-eb81ae61cdc8.png)




# おわりに
今回は、Terraform で複数 CodeBuild の複数環境変数をどう管理するかという一例をまとめました。
BUILD_PROJECT_C で利用しない環境変数を追加しなければいけない構造になっているのが何とも言えないというかもっとよくできる気がしますが...。
皆さんは Terraform でどのように複数 CodeBuild プロジェクトの複数環境変数を管理していますか？
自分ももっと綺麗に構築できるようIaaSを極めます。

明日の[CYBIRD Advent Calendar 2024](https://qiita.com/advent-calendar/2024/cybird) 15日目は、@cy-tatsuya-sakaiさんの「【Unity】シェイプマッチング弾性体の実装」です。
お楽しみに！