TerraformでCI/CDを構築する際、どこまでモジュール化すべき?
はじめに
AWSでCI/CD環境を構築する際、Terraformを使うと「IAMロール、S3、CodeBuild、CodePipeline...」と、リソースが芋づる式に増えていきます。
ここで直面するのが、「これ、どこまでひとまとめ(モジュール化)にするのが正解?」という問題です。今回は、私が実際に試行錯誤して辿り着いた「1パイプライン=1モジュール」という構成について紹介します。
結論から言うと、私は 「CodePipelineとその実行に必要なIAMロール・CodeBuildプロジェクトをまるごと1つのモジュールに閉じ込める」 という構成に落ち着きました。
なぜバラバラにしないのか?
最初は「IAMロールはIAMモジュールに」などと、リソースの種類ごとに分けようとしました。しかし、CodePipelineには以下の特徴があります。
密結合なIAMポリシー:
CodePipelineやCodeBuildのポリシーは、特定のS3バケットやCodeBuildプロジェクトに強く依存します。これらを別モジュールに分けると、モジュール間の値の受け渡し(OutputsとVariables)が複雑になりすぎてしまいます。
セットで管理したい:
「パイプラインをもう一つ増やしたい」となったとき、必要なリソースが1つのモジュールにまとまっていれば、module ブロックを1つ追加するだけで完結します。
実装例:main.tf の中身
今回作成した構成では、modules/codepipeline/ の中に、以下のリソースをすべて詰め込んでいます。
- CodePipeline本体
- CodeBuildプロジェクト
- 各種実行用IAMロール & ポリシー
このようにモジュール内で完結させることで、呼び出し側は、バケット名やプロジェクト名などの最小限の変数(Variables)を渡すだけで良くなります。
ディレクトリ構成
modules/
└── codepipeline/
├── main.tf <-- CodePipeline, CodeBuild, IAMを記述
├── variables.tf <-- パイプライン名やリポジトリ名などを定義
└── outputs.tf
呼び出し側
この構成にすると、利用側はこれだけで済みます。非常に宣言的で分かりやすくなります。
module "frontend_pipeline" {
source = "./modules/codepipeline"
project_name = "my-frontend"
artifact_s3_bucket = aws_s3_bucket.artifact.id
}
メリットとデメリット
この構成にしてみて感じたメリット・デメリットです。
| 項目 | メリット | デメリット |
|---|---|---|
| 可読性 | パイプラインに関わる設定が1箇所に集約される | IAM定義を含めると、1ファイルの行数が多くなりがち |
| 再利用性 | 他のプロジェクトや環境(stg/prod)への展開が容易 | 「このIAMロールだけ全パイプラインで共通化したい」という柔軟な変更には不向き |
まとめ
「どこまでモジュール化するか」の答えに正解はありませんが、「運用時にどこをセットで変更するか?」を考えると、自ずと境界線が見えてきます。
CI/CDの場合は、パイプラインの構成(ビルド手順や権限)が変わることが多いため、1つのモジュールにまとめて「独立してデプロイ・管理できる状態」にするのが、運用負荷を下げる近道だと感じました。
Discussion