想定読者
AWS CodeBuild と GitHub による Terraform の CI/CD 実装例を紹介します。
- Terraform の CI/CD ワークフローを実装したい人
- AWS CodeBuild の具体的な使用例をみたい人
構成図
CodeBuild のビルドプロジェクトを 2 つ作成します。
それぞれプルリクエスト作成/更新とマージに対応します。
terraform apply
が同時に実行される可能性がある場合は tfstate の破損を防ぐために DynamoDB による state lock を追加してください。
details GitHub Actionsについて
GitHub Actions を利用すると CI/CD に関連するリソースが GitHub に閉じるため、よりシンプルに利用できます。GitHub Enterprise(いわゆるオンプレ版)の利用が必須の場合、GitHub Actions を使うにはセルフホストランナーを用意する必要があります。インフラを自前で用意することが大変な場合、この記事のように AWS アカウントで CodeBuild を利用するという選択肢が考えられます。GitHub Actions であっても CI/CD の基本概念は同じため、参考にしてください。
tfnotify
Terraform の plan/apply 結果を GitHub や Slack に通知してくれるツールです。
事前準備
リポジトリ
GitHub にて新規にリポジトリを作成します。
プライベートリポジトリで問題ありません。
アクセストークン
GitHub にて 個人のアクセストークンを払い出します。
tfnotify が GitHub に通知をするために利用します。
Settings > Developer Settings > Personal access tokens > Tokens (classic)
より作成できます。
権限は repo:status
public_repo
のみで大丈夫です。
ウィンドウを閉じるとトークンは二度と確認できません。きちんとメモしておきましょう。
IAM
Terraform 実行に必要な権限を付与した IAM ロールを作成します。
本記事では IAM ポリシー AmazonS3FullAccess
を利用します。
信頼関係では CodeBuild を許可しましょう。
S3
Terraform のバックエンド用 S3 バケットを作成します。
デフォルト値から変える設定はありません。
構築
GitHub
サンプルコードを参考に、CI/CD 用のコードをプッシュします。
├── README.md
├── codebuild
│ ├── buildspec_apply.yml
│ ├── buildspec_plan.yml
│ └── tfnotify.yml
└─── environments
├── main.tf
└── provider.tf
CodeBuild が plan 時に利用する buildspec_plan.yml
は以下です。
version: 0.2
env:
variables:
TFDIR: "environments"
TFNCONF: "codebuild/tfnotify.yml"
TITLE: "Terraform Plan"
MSG: "Plan detail via tfnotify"
phases:
install:
commands:
# terraform
- curl -sL https://releases.hashicorp.com/terraform/1.6.2/terraform_1.6.2_linux_amd64.zip > terraform.zip
- unzip terraform.zip
- cp terraform /usr/local/bin
# tfnotify
- curl -sL https://github.com/mercari/tfnotify/releases/download/v0.8.0/tfnotify_linux_amd64.tar.gz > tfnotify.tar.gz
- tar -zxvf tfnotify.tar.gz
- cp tfnotify /usr/local/bin
pre_build:
commands:
- terraform -chdir="${TFDIR}" init -no-color
build:
commands:
- |
PLAN=$(terraform -chdir="${TFDIR}" plan -no-color 2>&1)
echo "${PLAN}" | tfnotify --config "${TFNCONF}" plan --title "${TITLE}" --message "${MSG}"
CodeBuild が apply 時に利用する buildspec_apply.yml
は以下です。
version: 0.2
env:
variables:
TFDIR: "environments"
TFNCONF: "codebuild/tfnotify.yml"
TITLE: "Terraform Apply"
MSG: "Apply detail via tfnotify"
phases:
install:
commands:
# terraform
- curl -sL https://releases.hashicorp.com/terraform/1.6.2/terraform_1.6.2_linux_amd64.zip > terraform.zip
- unzip terraform.zip
- cp terraform /usr/local/bin
# tfnotify
- curl -sL https://github.com/mercari/tfnotify/releases/download/v0.8.0/tfnotify_linux_amd64.tar.gz > tfnotify.tar.gz
- tar -zxvf tfnotify.tar.gz
- cp tfnotify /usr/local/bin
pre_build:
commands:
- terraform -chdir="${TFDIR}" init -no-color
build:
commands:
- |
APPLY=$(terraform -chdir="${TFDIR}" apply -auto-approve -no-color 2>&1)
echo "${APPLY}" | tfnotify --config "${TFNCONF}" apply --title "${TITLE}" --message "${MSG}"
CI/CD が頻繁に実行される場合は terraform
tfnotify
のインストールを毎回行わずに済むよう、カスタムイメージを利用しましょう。docker build
を行い ECR にプッシュすることで CodeBuild から利用可能です。
tfnotify 用の設定ファイルは以下です。
こちらは plan と apply を 1 ファイルにまとめられます。
---
ci: codebuild
notifier:
github:
token: $GITHUB_TOKEN
repository:
owner: "YOUR_GITHUB_USERNAME"
name: "ci-codebuild-terraform-20231026"
terraform:
plan:
template: |
{{ .Title }} <sup>[CI link]( {{ .Link }} )</sup>
{{ .Message }}
{{if .Result}}
<pre><code>{{ .Result }}
</pre></code>
{{end}}
<details><summary>Details (Click me)</summary>
<pre><code>{{ .Body }}
</pre></code></details>
apply:
template: |
{{ .Title }} <sup>[CI link]( {{ .Link }} )</sup>
{{ .Message }}
{{if .Result}}
<pre><code>{{ .Result }}
</pre></code>
{{end}}
<details><summary>Details (Click me)</summary>
<pre><code>{{ .Body }}
</pre></code></details>
CodeBuild
ビルドプロジェクトを 2 つ作成します。
以下に plan 用ビルドプロジェクトを作成するスクリーンショットを載せています。
apply 用のリソースのスクリーンショットは省略しますが、同様に作成します。
デフォルト値から変更していない箇所は、スクリーンショットを撮っていません。
名前やログ出力先などもビルドプロジェクトに合わせて適宜変更してください。
「プライマリソースのウェブフックイベント」が変わることに注意します。
- plan 用ビルドプロジェクト
-
PULL_REQUEST_CREATED
PULL_REQUEST_UPDATED
PULL_REQUEST_REOPEND
-
- apply 用ビルドプロジェクト
PULL_REQUEST_MERGED
サービスロールの編集を許可する場合は、自動的に IAM ロールに必要なポリシーがアタッチされます。許可しない場合、事前に必要な権限を付与しておく必要があります。
https://dev.classmethod.jp/articles/codebuild-service-role-checkbox/
個人用の AWS アカウントでない場合、アクセストークンはプレーンテキストではなく Secrets Manager を利用してください。
ストリーム名を空にすることで、ビルドごとに別のストリームにログを記録します。
2 つのビルドプロジェクトが作成できました。
CI/CD ワークフロー
実際に Terraform コードを記載し CI/CD ワークフローを回します。
今回は S3 バケットを作成します。 main.tf
に以下を記載してプッシュします。
resource "aws_s3_bucket" "my_bucket" {
bucket = "ci-codebuild-terraform-20231026-my-bucket"
}
プルリクエストを作成しました。
すると tfnotify
により plan 結果が通知されます。
レビュアーはこの結果を確認することで、マージ可否を簡単に判断できるようになります。
terraform init
や terraform plan
で失敗していないことが checks からも分かります。
レビュアーから Approve されたらマージしましょう。
マージ後 terraform apply
の結果が通知されます。
plan が通っても apply が失敗するケースはよくあるため、きちんと結果を確認しましょう。
以上、CI/CD ワークフローの一例でした。
まとめ
CodeBuild を使うことで、Terraform の CI/CD が実現できました。
プルリクエストをベースとすることで、作業ミスや認識齟齬をグッと減らすことができます。
また tfnotify は plan や apply 結果が プルリクエスト内で確認できるため、レビュー負荷が軽減されます。
CI/CD を未経験の方はぜひ試してみて欲しいです。