- 1 1. この記事について
- 2 2. 前提・環境・準備
- 3 3. CodePipeline V2 完全活用 (並列実行/条件分岐/変数/Triggers)
- 4 4. CodeDeploy ECS Blue/Green デプロイ完全構築
- 5 5. CodeDeploy Lambda Canary リリース戦略
- 6 6. Terraform完全実装 (Pipeline + Deploy + IAM + S3 + ECR 統合)
- 7 7. CloudWatch Alarm + SNS 通知 + 監査ログ
- 8 8. まとめ + Vol4予告 + よくある落とし穴10選
1. この記事について

- CodePipeline V2 新機能: 並列実行・条件分岐・変数サポート・Triggers で V1 を超える本番運用品質
- ECS Blue/Green デプロイ: ALB Target Group 切替 + CloudWatch Alarm auto-rollback を Terraform で完全実装
- Lambda Canary リリース: BeforeAllowTraffic/AfterAllowTraffic hooks + 段階デプロイ + 自動ロールバック
- Vol2 (CodeBuild) 連携: Build stage に CodeBuild project を組込む modular Terraform 構成
- 本番落とし穴10選: iam:PassRole 漏れ / LIVE alias 未設定 / auto-rollback alarm 設定ミス 等
1-1. 本記事のゴール
本記事を読み終えると、CodePipeline V2 + CodeDeploy を Terraform で本番投入できる完成度に達する。具体的には次の 3 点が実装できる状態になる。
- CodePipeline V2 パイプライン: Source (GitHub/CodeCommit) → Build (CodeBuild) → Test (並列) → Deploy (条件分岐) の stage を Terraform
aws_codepipelineで定義し、Variables と Triggers で動的に制御する - ECS Blue/Green デプロイ:
aws_codedeploy_deployment_groupで ALB Target Group の Blue→Green 切替を自動化し、CloudWatch Alarm 連動で失敗デプロイを自動ロールバックする - Lambda Canary リリース: Alias (
LIVE) に対してLambdaCanary10Percent10Minutes等の predefined config を適用し、AfterAllowTraffic hook で検証してから全量切替する
Vol2 (CodeBuild) で構築したビルド環境を Pipeline の Build stage に組込み、シリーズ通読でゼロから本番 CI/CD パイプラインを完走できる設計となっている。
1-2. 読者像
次のいずれかに当てはまるエンジニアを対象としている。
- Vol1/Vol2 読了者: AWS Code* ファミリーの全体像と CodeBuild の実践知識を持ち、次のステップとして本番デプロイ自動化を学びたい方
- ECS Fargate 運用者: 既に ECS でアプリを動かしており、Blue/Green デプロイへの移行で無停止リリースを実現したい方
- Lambda 関数リリース担当者: 本番 Lambda の Canary デプロイを hooks で品質保証しながら段階展開したい方
- Terraform IaC 実践者:
aws_codepipeline/aws_codedeploy_*リソースを modular 構成で管理したい方
前提知識: AWS コンソール操作の基礎 / Terraform 基礎 / ECS または Lambda の本番運用経験 / CodeBuild の基礎 (Vol2 相当)
1-3. なぜ今これを書くか
CodePipeline V2 は 2024 年に GA となったが、並列実行・条件分岐・変数・Triggers を実際の本番設計に活かした日本語記事は現時点でも稀少だ。V1 との差異を表形式で整理した上で、ECS Blue/Green と Lambda Canary という現場で最も需要の高い 2 大デプロイ戦略を 1 記事で統合解説するコンテンツはほとんど存在しない。
さらに Vol2 (CodeBuild) で構築したリソースを data source 参照でそのまま再利用する modular Terraform 構成は、シリーズを通読したエンジニアだけが得られる実用的な価値だ。本記事はその連携設計の完成形を提供する。
1-4. 本記事の章構成
各セクションは独立して参照できるが、初めて読む場合は §2→§3→§4 の順で進めると最も理解しやすい。
| § | タイトル | 難易度 | 所要時間 |
|---|---|---|---|
| §2 | 前提・環境・準備 | ★☆☆ | 20分 |
| §3 | CodePipeline V2 完全活用 | ★★☆ | 45分 |
| §4 | ECS Blue/Green デプロイ | ★★★ | 60分 |
| §5 | Lambda Canary リリース | ★★★ | 45分 |
| §6 | Terraform 完全実装 | ★★★ | 60分 |
| §7 | CloudWatch Alarm + 監査 | ★★☆ | 30分 |
| §8 | まとめ + 落とし穴10選 | ★☆☆ | 15分 |
このシリーズの学習ロードマップ
Vol1: AWS Code* ファミリー全体像・サービス選定判断ツリー
↓ どのサービスをどう使うか把握
Vol2: CodeBuild 完全活用 (buildspec / VPC / キャッシュ / Lambda compute)
↓ ビルド基盤を構築
Vol3 (本記事): CodePipeline V2 + CodeDeploy 本番デプロイ
↓ デプロイパイプラインを完成
Vol4: CodeArtifact + CodeGuru (予定)
パッケージ管理と品質分析
関連記事へのリンク
- ECS Fargate CI/CD Vol1 — Rolling デプロイ基礎 — §4 の前提として参考になる
- ECS Fargate Blue/Green デプロイ実践 — §4 と相互補完
1-5. 本記事で扱わないこと
本記事のスコープを明確にするため、意図的に扱わないトピックも記載する。
| 扱わないこと | 代替リソース |
|---|---|
| CodeCommit の詳細 (2024年新規顧客受付停止) | GitHub/CodeConnections を推奨 |
| GHA (GitHub Actions) との詳細比較 | Vol1 §3 で 7 軸比較マトリクスを提供 |
| ECS ローリングアップデート | ECS Fargate CI/CD Vol1 を参照 |
| Lambda の初回デプロイ設定 | AWS 公式ドキュメントを参照 |
| CodePipeline の料金計算 | Vol2 §7 コスト最適化の手法を参考に |
| マルチアカウント Cross-Account デプロイ | 本記事のシングルアカウント構成を基盤に拡張可能 |
これらは本記事のスコープを超えるが、Vol1〜Vol4 シリーズを通して学ぶことで体系的な知識が身につく設計となっている。
1-6. CodePipeline V2 と CodeDeploy の位置づけ
CodePipeline は CI/CD のオーケストレーターだ。「何を、どの順序で、どの条件で実行するか」を定義する。コードのビルド・テスト・デプロイの各フェーズを Stage として繋ぎ、V2 では並列実行と条件分岐で複雑なワークフローを宣言的に表現できる。
CodeDeploy はデプロイエンジンだ。「どのようにデプロイするか」の戦略 (Blue/Green, Canary, Linear, AllAtOnce) と、失敗時の自動ロールバックを担う。CodePipeline の Deploy stage から呼び出されるが、単独で使うこともできる (コンソールや CLI から直接 Deployment を作成可能)。
この役割分担を理解することで、どちらを設定すればよいか迷わなくなる。「パイプラインが動かない」トラブルの多くは、この2つの設定を混同することから発生する。
1-7. ECS Blue/Green vs Lambda Canary — 使い分け早見表
どちらのデプロイ戦略を選ぶかは、アプリケーションの種類とリリース要件によって決まる。
| 観点 | ECS Blue/Green | Lambda Canary |
|---|---|---|
| 対象 | コンテナアプリ (ECS Fargate/EC2) | Lambda 関数 |
| トラフィック制御 | ALB Target Group 切替 | Alias ウェイト調整 |
| 検証方法 | CloudWatch Alarm + WaitTimeInMinutes | hooks (BeforeAllowTraffic / AfterAllowTraffic) |
| ロールバック速度 | ALB ターゲット切替 (~30秒) | Alias ウェイト即時変更 (~5秒) |
| 並行稼働コスト | Blue+Green の 2 タスクセット稼働 | コストほぼゼロ (同一関数の割合変更) |
| 使用すべき場面 | Web API / マイクロサービス / ステートレスコンテナ | イベント駆動処理 / API Gateway バックエンド |
両方を同一パイプラインで扱う場合:
ECS サービスと Lambda 関数の両方をデプロイするモノリス分解過程のシステムでは、CodePipeline の Deploy stage を並列に設定し、ECS 用と Lambda 用の CodeDeploy アプリケーションを個別に呼び出すことができる。§6 の Terraform でこのパターンを実装する。
どちらの戦略を使う場合も、auto-rollback の CloudWatch Alarm を必ず設定するのが本番運用の大原則だ。設定を省略すると、異常なデプロイが完走してしまいインシデントが拡大する。§8 の落とし穴10選でも詳しく取り上げる。
2. 前提・環境・準備

2-1. 前提環境
本記事のハンズオンを完走するには以下の環境が必要だ。
| 項目 | 要件 | 備考 |
|---|---|---|
| AWS アカウント | 管理者権限または以下のポリシーを持つ IAM ユーザー/ロール | AdministratorAccess 推奨 (学習環境) |
| Terraform | 1.9.x 以上 | terraform version で確認 |
| AWS CLI | v2.x | aws --version で確認 |
| Docker | 24.x 以上 | ECR push に使用 |
| Git | 任意 | GitHub/CodeCommit 接続時 |
必要 IAM 権限 (最小権限)
codepipeline:*
codedeploy:*
codebuild:*
ecs:*
lambda:*
iam:PassRole
iam:CreateRole
iam:AttachRolePolicy
s3:*
ecr:*
cloudwatch:*
events:*
sns:*
logs:*
iam:PassRole は CodePipeline が CodeDeploy/CodeBuild に実行ロールを渡す際に必須。この権限が不足すると「User is not authorized to perform: iam:PassRole」エラーが発生し、パイプラインが起動できない。落とし穴ランキング第1位なので特に注意する。
# 必要権限確認 (AWS CLI)
aws iam simulate-principal-policy \
--policy-source-arn "arn:aws:iam::$(aws sts get-caller-identity --query Account --output text):user/YOUR_USER" \
--action-names "iam:PassRole" \
--resource-arns "arn:aws:iam::*:role/*"
コンソール確認手順:
1. IAM コンソール → ユーザー/ロールを選択
2. 「アクセス許可」タブ → 「アクセス許可をシミュレート」
3. アクション入力欄に iam:PassRole → 「シミュレートを実行」
2-2. 使用技術スタック
| サービス/ツール | バージョン/種別 | 用途 |
|---|---|---|
| AWS CodePipeline | V2 (2024 GA) | CI/CD パイプライン オーケストレーション |
| AWS CodeDeploy | ECS / Lambda | Blue/Green・Canary デプロイエンジン |
| AWS CodeBuild | 任意 (Vol2 参照) | Build stage — Docker image build + ECR push |
| ECS Fargate + ALB | — | Blue/Green デプロイ対象サービス |
| Lambda + Alias | — | Canary デプロイ対象関数 |
| Terraform | aws provider 5.x | IaC 全リソース定義 |
| S3 | — | Pipeline artifact store |
| ECR | — | Docker image registry |
| CloudWatch Alarms | — | auto-rollback トリガー |
| SNS | — | デプロイ成功/失敗通知 |
| EventBridge | — | CodeDeploy イベント → SNS |
Vol2 (CodeBuild) との連携
本記事の §6 Terraform では、Vol2 で作成した CodeBuild project を data source で参照してパイプラインの Build stage に組込む。Vol2 を先に構築していることを前提とするが、Vol2 未実施の場合は aws_codebuild_project を §6 内でインラインで定義する代替 HCL も提供する。
# Vol2 CodeBuild project を data source で参照
data "aws_codebuild_project" "app_build" {
name = "${var.project}-build" # Vol2 で作成したプロジェクト名
}
2-3. ゴール状態の定義
本記事のハンズオンを完走すると、以下の状態が実現できる。
ECS Blue/Green パイプライン (§3-§4)
– GitHub push → CodePipeline V2 起動 (Triggers: branch filter)
– Build stage: CodeBuild で Docker image をビルドし ECR に push
– Deploy stage: CodeDeploy が ECS タスク定義を更新し ALB Target Group を Blue→Green 切替
– 検証待ち時間: WaitTimeInMinutes でトラフィック切替後の安定確認
– auto-rollback: CloudWatch Alarm (ECSServiceCPUHigh 等) が ALARM → 旧バージョンへ自動切戻し
– 変数: #{Variables.ImageTag} で Build stage の ECR タグを Deploy stage に受け渡し
Lambda Canary パイプライン (§5)
– Lambda 関数コード変更 → Pipeline 起動
– CodeDeploy: LIVE alias に対して LambdaCanary10Percent10Minutes config を適用
– 10分間 10% トラフィックで BeforeAllowTraffic / AfterAllowTraffic hooks が自動実行
– 検証 PASS → 残り 90% 切替 / 検証 FAIL → 旧バージョンへ自動ロールバック
モニタリング・監査 (§7)
– EventBridge: デプロイ状態変化 → SNS → メール/Slack 通知
– CloudTrail: すべての API コール記録 (S3 バケット保存)
– CloudWatch Dashboard: デプロイ頻度・成功率・ロールバック件数を可視化
2-4. Terraform ディレクトリ構成
本記事では以下の modular 構成を使用する。§6 で完全な HCL を提供するが、実行前に構造を把握しておくとスムーズだ。
terraform/
├── pipeline/
│├── main.tf # provider, backend
│├── variables.tf # 変数定義
│├── pipeline.tf# aws_codepipeline (V2)
│├── codedeploy.tf # aws_codedeploy_app + deployment_group
│├── iam.tf # CodePipeline/CodeDeploy Service Role
│├── s3.tf# artifact store S3
│└── outputs.tf # pipeline ARN 等の出力
└── modules/
└── codebuild/ # Vol2 から参照 (data source)
# 初期化 + 実行計画確認
cd terraform/pipeline
terraform init
terraform plan -out=tfplan
# 適用
terraform apply tfplan
# 作成されたリソース確認
terraform state list | grep -E "codepipeline|codedeploy"
2-5. IAM Role 設計
本ハンズオンでは以下の 3 つの IAM Role を作成する。各ロールの信頼ポリシーと権限を正しく設定しないと、パイプラインが途中で停止するトラブルが頻発する。
| IAM Role | 信頼主体 | 主な権限 | 用途 |
|---|---|---|---|
CodePipelineServiceRole | codepipeline.amazonaws.com | S3/CodeBuild/CodeDeploy/iam:PassRole | Pipeline 全体の実行ロール |
CodeDeployServiceRole | codedeploy.amazonaws.com | ECS/Lambda/S3/CloudWatch/SNS | CodeDeploy が各 AWS サービスを操作するロール |
CodeBuildServiceRole | codebuild.amazonaws.com | ECR/S3/Logs/CodeBuild | Build stage が Docker build + ECR push するロール (Vol2 参照) |
CodePipelineServiceRole — 信頼ポリシー
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": { "Service": "codepipeline.amazonaws.com" },
"Action": "sts:AssumeRole"
}
]
}
CodePipelineServiceRole — 許可ポリシー (要点)
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject", "s3:PutObject", "s3:GetBucketVersioning",
"codebuild:BatchGetBuilds", "codebuild:StartBuild",
"codedeploy:CreateDeployment", "codedeploy:GetDeployment",
"codedeploy:GetDeploymentConfig", "codedeploy:RegisterApplicationRevision",
"iam:PassRole"
],
"Resource": "*"
}
]
}
iam:PassRole は Resource: "*" にしておかないと、CodePipeline が CodeDeploy のサービスロールを渡す際に失敗する。Terraform では aws_iam_role_policy または aws_iam_role_policy_attachment で設定する (§6 で完全 HCL を提供)。
CodeDeployServiceRole — 信頼ポリシー
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": { "Service": "codedeploy.amazonaws.com" },
"Action": "sts:AssumeRole"
}
]
}
ECS Blue/Green 用には AWS 管理ポリシー AWSCodeDeployRoleForECS を付与する。Lambda Canary 用には AWSCodeDeployRoleForLambda を使用する。両方のデプロイ対象がある場合は両ポリシーを同一ロールに付与してよい。
# CodeDeploy Service Role 作成 (AWS CLI)
aws iam create-role \
--role-name CodeDeployServiceRole \
--assume-role-policy-document file://codedeploy-trust.json
# ECS Blue/Green 用ポリシー付与
aws iam attach-role-policy \
--role-name CodeDeployServiceRole \
--policy-arn arn:aws:iam::aws:policy/AWSCodeDeployRoleForECS
# Lambda Canary 用ポリシー付与
aws iam attach-role-policy \
--role-name CodeDeployServiceRole \
--policy-arn arn:aws:iam::aws:policy/AWSCodeDeployRoleForLambda
コンソール操作手順:
1. IAM コンソール → 「ロール」→「ロールを作成」
2. 信頼エンティティ: 「AWS サービス」→「CodeDeploy」を選択
3. ユースケース: 「CodeDeploy – ECS」を選択 → AWSCodeDeployRoleForECS が自動付与
4. ロール名: CodeDeployServiceRole → 作成完了
3. CodePipeline V2 完全活用 (並列実行/条件分岐/変数/Triggers)
CodePipeline V2 は 2024 年に GA となった大幅アップデートで、V1 では不可能だった並列実行・条件分岐・変数サポート・Triggers が一気に追加された。本セクションでは V1 との差異を整理した上で、本番環境で実際に役立つ設計パターンを Terraform / AWS CLI / コンソール の 3 形式で解説する。
| 機能 | V1 | V2 | 本番活用ポイント |
|---|---|---|---|
| pipelineType 設定 | V1 (デフォルト) | V2 (明示指定必須) | 既存パイプラインは V1 のため新規作成または再設定が必要 |
| Action 並列実行 | runOrder 同値で擬似並列 | Stage 内 parallel actions (真の並列) | Build + Unit Test を同一 Stage で並列化しビルド時間を短縮 |
| 条件分岐 | なし | stage conditions (onEntry / onSuccess / onFailure) | Staging 成功時のみ Production へ進む guardrail を宣言的に設計 |
| 変数サポート | なし | namespace + PipelineVariable + #{Var} | ECR イメージタグや環境名を Stage 間で受け渡し可能 |
| Triggers フィルタ | 全ブランチまたは CodeCommit 限定 | branch / tag / path / PR フィルタ (CodeConnections) | mono-repo で変更パスごとにパイプライン起動を分離 |
| 実行履歴 UI | 逐次実行履歴のみ | 並列ステージ可視化グラフ | 並列 Stage の進捗をリアルタイム追跡 |
注意: V1 → V2 へ変更すると Pipeline 内部状態がリセットされ、進行中の実行は強制終了する。本番 Pipeline の移行は必ずメンテナンスウィンドウで実施すること。

3-1. PipelineType V2 の設定
V2 の全機能を有効化するには pipeline_type = "V2" を明示する。省略すると V1 として扱われ、並列実行・条件分岐・変数の設定項目が UI にも API にも現れない。
Terraform 実装
resource "aws_codepipeline" "main" {
name = "${var.project}-pipeline"
role_arn= aws_iam_role.codepipeline.arn
pipeline_type = "V2"# V2 機能を有効化するために必須
artifact_store {
location = aws_s3_bucket.artifact.bucket
type = "S3"
encryption_key {
id= aws_kms_key.artifact.arn
type = "KMS"
}
}
variable {
name = "ImageTag"
default_value = "latest"
description= "ECR image tag passed from Build stage"
}
}
AWS CLI
# 既存パイプラインを V2 へ移行 (pipeline-v2.json で "pipelineType": "V2" を指定)
aws codepipeline update-pipeline \
--pipeline file://pipeline-v2.json
コンソール操作: [CodePipeline] → 対象パイプラインを開く → [設定の編集] → パイプラインタイプ: V2 を選択 → [保存]。
3-2. 並列実行 (Parallel Actions in Stage)
同一 Stage 内に複数の Action を定義し、run_order を同値にすると並列起動される。V2 では宣言的に並列化でき、特別な設定は不要だ。
Terraform 実装
stage {
name = "Build"
action {
name = "BuildApp"
category= "Build"
owner= "AWS"
provider= "CodeBuild"
version = "1"
run_order = 1
input_artifacts = ["SourceArtifact"]
output_artifacts = ["BuildArtifact"]
configuration = { ProjectName = aws_codebuild_project.app.name }
}
action {
name = "UnitTest"
category= "Build"
owner= "AWS"
provider= "CodeBuild"
version = "1"
run_order = 1# BuildApp と同値 → 並列実行
input_artifacts = ["SourceArtifact"]
output_artifacts = ["TestReport"]
configuration = { ProjectName = aws_codebuild_project.unittest.name }
}
}
run_order = 1 を両 Action に設定すると 2 つの CodeBuild ジョブが同時起動する。片方が失敗すると Stage 全体が FAILED となり後続 Stage への進行が止まる。逐次実行が必要な場合は後続 Action の run_order = 2 に変更する。
コンソール操作: Stage の編集 → アクションを追加 → 実行順序に 1 (同値) を設定 → 保存。
3-3. Conditions (条件分岐)
V2 の Stage には on_entry / on_success / on_failure の 3 タイプの condition を定義できる。「Staging PASS 時のみ Production Stage へ進む」「CloudWatch Alarm が ALARM 状態なら自動 ROLLBACK」といった制御を宣言的に実装できる。
Terraform 実装 (on_failure で Alarm 連動 ROLLBACK)
stage {
name = "Production"
on_failure {
result = "ROLLBACK"
condition {
result = "ROLLBACK"
rules {
name = "PostDeployAlarm"
rule_type_id {
category = "Rule"
owner = "AWS"
provider = "CloudWatchAlarmRule"
version = "1"
}
configuration = {
AlarmName = aws_cloudwatch_alarm.post_deploy_error.name
}
}
}
}
# action ブロックは §4 で詳述
}
on_failure に result = "ROLLBACK" を設定すると、デプロイ後に CloudWatch Alarm が ALARM 状態になった場合に CodeDeploy の auto-rollback が起動する。on_entry に result = "SKIP" を設定すると条件未達の Stage をスキップして次 Stage へ進める。
AWS CLI でパイプライン実行状態を確認
aws codepipeline get-pipeline-execution \
--pipeline-name "${PROJECT}-pipeline" \
--pipeline-execution-id "${EXECUTION_ID}" \
--query 'pipelineExecution.status'
3-4. Variables (変数サポート: namespace + PipelineVariable)
V2 では Action の出力変数を #{Namespace.VariableName} 構文で後続 Action / Stage から参照できる。最も典型的な用途は、CodeBuild で決定した ECR イメージタグを Deploy Stage へ受け渡すパターンである。
Terraform 実装 (変数の定義・export・参照)
# パイプラインレベルで変数を定義
variable {
name = "ImageTag"
default_value = "latest"
}
# Build Stage: namespace を指定して変数を export
action {
name= "BuildAndPush"
namespace = "BuildVars"# この名前空間の変数を後続 Stage から参照可能
# (category / provider 等の設定は §3-1 Terraform 参照)
}
# Deploy Stage: #{Namespace.VarName} で参照
action {
name = "DeployECS"
configuration = {
Image1Tag = "#{BuildVars.IMAGE_TAG}"# Build Stage から渡されたイメージタグ
# (他の configuration は §4 Terraform 参照)
}
}
CodeBuild の buildspec.yml 側では export_variables セクションに IMAGE_TAG を定義する。namespace = "BuildVars" と対応していれば Stage 間で参照可能。変数スコープは同一 Stage 内が基本のため、Stage をまたぐ場合は Pipeline 変数 (variable ブロック) に昇格させる。
AWS CLI で実行時に変数を上書き注入
aws codepipeline start-pipeline-execution \
--name "${PROJECT}-pipeline" \
--variables name=ImageTag,value=v1.2.3
コンソール操作: [実行を開始] → [変数のオーバーライド] → ImageTag = v1.2.3 を入力 → 開始。
3-5. Triggers (branch / tag / path フィルタ + CodeConnections)
V2 の trigger ブロックで push / pull_request を細粒度にフィルタリングできる。mono-repo で services/api/ と services/worker/ が混在する場合、それぞれ別 Pipeline を起動するパスフィルタを設定することで無関係なビルドを防げる。
Terraform 実装
trigger {
provider_type = "CodeStarSourceConnection"
git_configuration {
source_action_name = "Source"
push {
branches {
includes = ["main", "release/*"]
excludes = ["release/hotfix-*"]
}
file_paths {
includes = ["services/api/**", "infra/**"]
excludes = ["docs/**", "*.md", "*.txt"]
}
}
pull_request {
events = ["OPEN", "UPDATED"]
branches {
includes = ["main"]
}
file_paths {
includes = ["services/api/**"]
}
}
}
}
CodeConnections の接続 ARN を確認
aws codeconnections list-connections \
--provider-type GitHub \
--query 'Connections[?ConnectionStatus==`AVAILABLE`].{Name:ConnectionName,Arn:ConnectionArn}' \
--output table
接続が AVAILABLE になっていないとトリガーが動作しない。新規接続を作成した場合はコンソールで OAuth 認可を完了させてから Terraform apply を実行すること。
コンソール操作: ソースステージの編集 → [GitHub (CodeConnections)] → ブランチフィルタ / ファイルパスフィルタ を入力 → 保存。
3-6. 落とし穴と対策
| 落とし穴 | 現象 | 対策 |
|---|---|---|
| pipelineType を省略して V1 のまま運用 | 条件分岐・変数の設定項目が現れない | pipeline_type = "V2" を必ず明示 |
| V1 → V2 移行時に進行中の実行が消える | 実行中の Stage が ABORTED になる | メンテナンスウィンドウで移行し移行後に再実行 |
| 並列 Action 間の output_artifacts 名が重複 | apply エラーまたは意図しないアーティファクト参照 | output_artifacts 名は Action ごとにユニークにする |
| 変数の namespace スコープを誤って Stage 間で直接参照 | 実行時エラー: 変数未定義 | Pipeline 変数に昇格させてから参照する |
| file_paths の excludes 未設定 | docs 更新のたびにパイプラインが無駄起動 | excludes = ["docs/**", "*.md"] を設定 |
| CodeConnections が PENDING のまま Terraform apply | Source Action 認証エラーでトリガー不動作 | コンソールで OAuth 認可を完了してから apply |
4. CodeDeploy ECS Blue/Green デプロイ完全構築

- Canary10Percent5Minutes: トラフィックの 10% を Green (新バージョン) に流し 5 分間監視、問題なければ残り 90% を一括切替 → 本番推奨
- Linear10PerMinuteFor10Minutes: 毎分 10% ずつ 10 分かけて段階移行 → SLA が厳格な金融・医療系環境向け
- AllAtOnce: 即時 100% 切替 → ステージング・内部検証環境向け
- 切替は ALB Listener Rule で Blue Target Group → Green Target Group → 確認後に Blue を終了
- CloudWatch Alarm が ALARM 状態になると
DEPLOYMENT_STOP_ON_ALARMで自動ロールバック実行 deployment_style.deployment_option = "WITH_TRAFFIC_CONTROL"を必ず設定すること (未設定では traffic shifting が無効化される)
4-1. ECS Blue/Green デプロイの全体像
ECS Blue/Green デプロイは、現在稼働中のタスク (Blue) と新バージョンのタスク (Green) を並列で起動し、ALB Listener Rule を切り替えてトラフィックを段階的に移行する手法だ。ダウンタイムゼロでリリースでき、問題が発覚すれば数秒で元の Blue 環境に切り戻せる。
関連記事: ECS Fargate CI/CD Vol1 / ECS Blue/Green 詳解
主要コンポーネントの役割:
| コンポーネント | 役割 | 備考 |
|---|---|---|
| CodeDeploy Application | デプロイ対象の論理グループ | compute_platform = "ECS" |
| DeploymentGroup | ECS Service + ALB Listener の紐付け | Blue/Green 設定を内包 |
| DeploymentConfig | traffic shifting 方式の指定 | Canary / Linear / AllAtOnce |
| ALB Target Group (Blue) | 現行バージョンへのトラフィック | 本番 Listener (443) が向く |
| ALB Target Group (Green) | 新バージョンへのトラフィック | デプロイ中に CodeDeploy が切替 |
| appspec.yaml | デプロイ手順定義 | TaskDefinition + LoadBalancerInfo |
| imageDetail.json | 新コンテナイメージの URI | CodeBuild が生成・CodePipeline が渡す |
| taskdef.json | ECS タスク定義 JSON | <IMAGE1_NAME> を imageDetail.json で置換 |
ECS Blue/Green はコンテナイメージの差し替え + ALB 切替のみが対象のため、ECS Service の deployment_type を Terraform で CODE_DEPLOY に設定する必要がある。CloudFormation 型の Blue/Green と混同しないこと。
4-2. ALB Target Group 設計 (Blue + Green 2 セット)
ECS Blue/Green デプロイでは ALB Target Group を必ず 2 セット用意する。1 Target Group のみでは CodeDeploy が切替先を確保できずデプロイ失敗となる。Blue と Green は同一構成で name のみ異なる。
resource "aws_lb_target_group" "blue" {
name = "${var.service_name}-blue"
port = 8080
protocol = "HTTP"
vpc_id= var.vpc_id
target_type = "ip"
health_check { path = "/health"; healthy_threshold = 2; interval = 30 }
}
resource "aws_lb_target_group" "green" {
name = "${var.service_name}-green"
port = 8080; protocol = "HTTP"; vpc_id = var.vpc_id; target_type = "ip"
health_check { path = "/health"; healthy_threshold = 2; interval = 30 }
}
resource "aws_lb_listener" "prod" {
load_balancer_arn = aws_lb.main.arn
port = 443; protocol = "HTTPS"
ssl_policy= "ELBSecurityPolicy-TLS13-1-2-2021-06"
certificate_arn = var.acm_certificate_arn
default_action { type = "forward"; target_group_arn = aws_lb_target_group.blue.arn }
}
resource "aws_lb_listener" "test" {
load_balancer_arn = aws_lb.main.arn
port = 8443; protocol = "HTTPS"
ssl_policy= "ELBSecurityPolicy-TLS13-1-2-2021-06"
certificate_arn = var.acm_certificate_arn
default_action { type = "forward"; target_group_arn = aws_lb_target_group.green.arn }
}
本番 Listener (port 443) は Blue Target Group、テスト Listener (port 8443) は Green Target Group を向く。CodeDeploy はデプロイ中に本番 Listener の向き先を Green に切り替え、termination_wait_time_in_minutes 経過後に Blue タスクを終了する。
4-3. CodeDeploy Application / DeploymentGroup 設定
resource "aws_codedeploy_app" "ecs" {
compute_platform = "ECS"
name = "${var.service_name}-deploy-app"
}
resource "aws_codedeploy_deployment_group" "ecs" {
app_name= aws_codedeploy_app.ecs.name
deployment_group_name = "${var.service_name}-dg"
service_role_arn = aws_iam_role.codedeploy.arn
deployment_config_name = "CodeDeployDefault.ECSCanary10Percent5Minutes"
deployment_style {
deployment_option = "WITH_TRAFFIC_CONTROL"
deployment_type= "BLUE_GREEN"
}
ecs_service {
cluster_name = var.ecs_cluster_name
service_name = var.ecs_service_name
}
load_balancer_info {
target_group_pair_info {
prod_traffic_route {
listener_arns = [aws_lb_listener.prod.arn]
}
test_traffic_route {
listener_arns = [aws_lb_listener.test.arn]
}
target_group {
name = aws_lb_target_group.blue.name
}
target_group {
name = aws_lb_target_group.green.name
}
}
}
blue_green_deployment_config {
deployment_ready_option {
action_on_timeout = "CONTINUE_DEPLOYMENT"
wait_time_in_minutes = 0
}
terminate_blue_instances_on_deployment_success {
action= "TERMINATE"
termination_wait_time_in_minutes = 10
}
}
auto_rollback_configuration {
enabled = true
events = ["DEPLOYMENT_FAILURE", "DEPLOYMENT_STOP_ON_ALARM"]
}
alarm_configuration {
alarms = [aws_cloudwatch_metric_alarm.ecs_5xx.name]
enabled = true
}
}
deployment_style.deployment_option = "WITH_TRAFFIC_CONTROL" は必須。省略すると ALB による traffic shifting が無効になり、ECS Blue/Green の恩恵が得られない。terminate_blue_instances_on_deployment_success.termination_wait_time_in_minutes に 10 分を設定することで、Green 確認後のロールバック猶予を確保できる。
4-4. appspec.yaml / taskdef.json / imageDetail.json
ECS Blue/Green デプロイでは 3 つのファイルを CodePipeline の artifact として渡す。
appspec.yaml — CodeDeploy が参照するデプロイ手順定義:
version: 0.0
Resources:
- TargetService:
Type: AWS::ECS::Service
Properties:
TaskDefinition: <TASK_DEFINITION>
LoadBalancerInfo:
ContainerName: "app"
ContainerPort: 8080
PlatformVersion: "LATEST"
NetworkConfiguration:
AwsvpcConfiguration:
Subnets:
- "subnet-xxxxxxxx"
- "subnet-yyyyyyyy"
SecurityGroups:
- "sg-xxxxxxxx"
AssignPublicIp: "DISABLED"
<TASK_DEFINITION> プレースホルダは CodeDeploy が taskdef.json の内容で自動置換する。
imageDetail.json — CodeBuild が出力する新コンテナイメージの URI:
{
"ImageURI": "123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/my-app:abc1234"
}
taskdef.json — ECS タスク定義 JSON。<IMAGE1_NAME> を imageDetail.json の URI で自動置換する。containerDefinitions[].name は appspec.yaml の ContainerName と完全一致させること。
CodePipeline の Deploy Stage では InputArtifacts に BuildOutput を指定し、appspec.yaml / taskdef.json / imageDetail.json の 3 ファイルをまとめて渡す。CodeBuild の buildspec.yaml で artifacts.files にこれら 3 ファイルを含めること。
4-5. auto-rollback (CloudWatch Alarm 連動)
CloudWatch Alarm が ALARM 状態になると DEPLOYMENT_STOP_ON_ALARM イベントでデプロイを停止し、自動ロールバックを実行する。監視対象は Green Target Group の 5xx エラーレートが推奨だ。
resource "aws_cloudwatch_metric_alarm" "ecs_5xx" {
alarm_name = "${var.service_name}-green-5xx-rate"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = 2
metric_name= "HTTPCode_Target_5XX_Count"
namespace = "AWS/ApplicationELB"
period = 60
statistic = "Sum"
threshold = 10
dimensions = {
LoadBalancer = aws_lb.main.arn_suffix
TargetGroup = aws_lb_target_group.green.arn_suffix
}
alarm_description = "Green Target Group の 5xx エラーが閾値超過 → auto-rollback"
treat_missing_data = "notBreaching"
}
auto-rollback イベントは 2 種類:
| イベント | トリガー | 対策 |
|---|---|---|
DEPLOYMENT_FAILURE | ECS タスク起動失敗・ヘルスチェック失敗 | タスク定義の cpu/memory 不足・IAM 権限不足を確認 |
DEPLOYMENT_STOP_ON_ALARM | CloudWatch Alarm が ALARM 状態 | Alarm 閾値が低すぎると誤検知ロールバックが発生 → evaluation_periods で調整 |
ロールバック時は ALB Listener が Blue Target Group に戻り、Green タスクが終了する。termination_wait_time_in_minutes 内であれば即座に切り戻し可能。0 に設定すると Blue タスクがデプロイ成功直後に終了するためロールバック不能になる点に注意。
4-6. AWS CLI / コンソール操作
AWS CLI: デプロイ実行とステータス確認
DEPLOYMENT_ID=$(aws deploy create-deployment \
--application-name my-ecs-app \
--deployment-group-name my-ecs-dg \
--s3-location bucket=my-pipeline-artifacts,key=deploy/artifacts.zip,bundleType=zip \
--query 'deploymentId' --output text)
aws deploy get-deployment \
--deployment-id "$DEPLOYMENT_ID" \
--query 'deploymentInfo.{Status:status,ErrorCode:errorInformation.code}' \
--output table
コンソール操作手順:
- CodeDeploy → [アプリケーション作成] → コンピューティングプラットフォーム: Amazon ECS
- [デプロイグループ作成] → サービスロール:
CodeDeployRole(AWSCodeDeployRoleForECS付与済み) - ECS クラスター・サービス名を選択
- ALB・本番リスナー (443)・テストリスナー (8443)・Target Group (Blue/Green) を設定
- デプロイ設定:
CodeDeployDefault.ECSCanary10Percent5Minutes - ロールバック → 「失敗時に自動ロールバック」有効化 → CloudWatch アラーム名を入力
- Blue/Green 設定 → 「元のバージョンの終了時間」: 10 分
4-7. 落とし穴
| # | 落とし穴 | 症状 | 対策 |
|---|---|---|---|
| 1 | ALB Target Group が 1 つだけ | デプロイ失敗: No valid target groups | Blue/Green 各 1 セット必ず作成 |
| 2 | deployment_option が WITHOUT_TRAFFIC_CONTROL | traffic shifting なしで即時全切替 | WITH_TRAFFIC_CONTROL 必須 |
| 3 | termination_wait_time が 0 | ロールバック不能 (Blue タスクが即終了) | 最低 5-10 分確保 |
| 4 | appspec.yaml の ContainerName がタスク定義と不一致 | デプロイ失敗: container not found | taskdef.json の containerDefinitions[].name と完全一致させる |
| 5 | CodeDeploy IAM Role に ecs:DescribeServices が欠如 | サービス検出失敗 | AWS 管理ポリシー AWSCodeDeployRoleForECS を使用 |
| 6 | ECS Service の deployment_controller が ECS のまま | Blue/Green 非対応エラー | deployment_controller { type = "CODE_DEPLOY" } に変更 |
5. CodeDeploy Lambda Canary リリース戦略

Lambda Canary デプロイの成否を分けるのは Alias 設計 と hooks 関数の品質 の2点だ。$LATEST を直接 traffic shift の対象にすることはできない。必ず LIVE のような固定 Alias を用意し、バージョン番号を明示管理せよ。BeforeAllowTraffic / AfterAllowTraffic の両 hooks が成功を返すまで CodeDeploy はトラフィック切替を進めない。CloudWatch Alarm は Alias ディメンション (FunctionName:LIVE) で設定しないと Canary 分のメトリクスが計上されず auto-rollback が機能しない点に注意。
- 新バージョン ($LATEST を publish → 明示バージョン番号) を作成
- CodeDeploy が DeploymentGroup を検知 → BeforeAllowTraffic hook を起動
- hook 関数がスモークテスト/DB接続確認を実施 → Succeeded 返却
- Canary 割合 (例: 10%) で LIVE Alias のトラフィックを新バージョンへ shift
- CloudWatch Alarm を監視 → エラー率上昇で auto-rollback、正常なら AfterAllowTraffic hook へ
- AfterAllowTraffic hook が合否判定 → Succeeded で 100% 切替完了
5-1. Lambda Alias + Versioning 設計
Lambda の Canary デプロイでは $LATEST を直接デプロイ対象にすることはできない。CodeDeploy は Alias に紐付いたバージョン間の traffic shift を行うため、以下の設計が必須だ。
| 設計要素 | 推奨値 | 理由 |
|---|---|---|
| Alias 名 | LIVE | 固定名で CodeDeploy DeploymentGroup に紐付け |
| バージョン管理 | 明示バージョン番号 (1, 2, 3…) | $LATEST は traffic shift 不可 |
| publish = true | Terraform で必須 | 省略すると $LATEST のみ更新されバージョン番号が増えない |
| デプロイ前バージョン | LIVE Alias の現行ポインタ | rollback 時の戻り先 |
| デプロイ後バージョン | 新規 publish バージョン | shift 先 |
resource "aws_lambda_function" "my_function" {
function_name = "my-production-function"
handler = "index.handler"
runtime = "nodejs20.x"
publish = true # 明示バージョン管理に必須
role = aws_iam_role.lambda_exec.arn
filename= "function.zip"
}
resource "aws_lambda_alias" "live" {
name = "LIVE"
function_name = aws_lambda_function.my_function.arn
function_version = aws_lambda_function.my_function.version
# CodeDeploy が routing_config を動的に上書きするため
# initial は current_version のみ指定 (routing_config ブロック追加不要)
}
Alias LIVE を API Gateway / ALB のターゲットとして設定することで、エンドユーザーへの影響をトラフィック割合で制御できる。
5-2. appspec.yml Lambda 形式と hooks 実装
Lambda デプロイ用 appspec.yml は ECS 用と書式が異なる。Resources ブロックで現在バージョンと新バージョン、Hooks ブロックで検証 Lambda 関数名を指定する。
version: 0.0
Resources:
- MyFunction:
Type: AWS::Lambda::Function
Properties:
Name: "my-production-function"
Alias: "LIVE"
CurrentVersion: "1"
TargetVersion: "2"
Hooks:
- BeforeAllowTraffic: "smoke-test-function"
- AfterAllowTraffic: "validation-function"
BeforeAllowTraffic hooks 実装例 — トラフィック切替前のスモークテスト:
import boto3
import json
def handler(event, context):
deployment_id = event['DeploymentId']
hook_execution_id = event['LifecycleEventHookExecutionId']
codedeploy = boto3.client('codedeploy')
try:
lambda_client = boto3.client('lambda')
# 新バージョンに直接 invoke して動作確認 ($LATEST ではなく明示バージョン番号)
response = lambda_client.invoke(
FunctionName='my-production-function:2',
InvocationType='RequestResponse',
Payload=json.dumps({'source': 'smoke-test'})
)
if response['StatusCode'] != 200:
raise ValueError(f"Unexpected status: {response['StatusCode']}")
codedeploy.put_lifecycle_event_hook_execution_status(
deploymentId=deployment_id,
lifecycleEventHookExecutionId=hook_execution_id,
status='Succeeded'
)
except Exception as e:
print(f"Smoke test failed: {e}")
codedeploy.put_lifecycle_event_hook_execution_status(
deploymentId=deployment_id,
lifecycleEventHookExecutionId=hook_execution_id,
status='Failed'
)
raise
hooks 関数には codedeploy:PutLifecycleEventHookExecutionStatus 権限を IAM で付与すること。この権限が漏れると hooks が 15 分のタイムアウトまで待機し続け、そのまま Failed 扱いになる。
5-3. デプロイ設定の選択 (Canary / Linear / AllAtOnce)
CodeDeploy が提供する Lambda 用 predefined config は次のとおりだ。本番では Canary または Linear を選択し、障害検知時間を稼ぐ設計が鉄則だ。
| Config 名 | トラフィック推移 | Canary 待機 | 推奨用途 |
|---|---|---|---|
| LambdaCanary10Percent5Minutes | 10% → 100% | 5 分 | 低頻度リクエスト API |
| LambdaCanary10Percent10Minutes | 10% → 100% | 10 分 | 標準本番 API |
| LambdaCanary10Percent15Minutes | 10% → 100% | 15 分 | 高リスク変更 |
| LambdaLinear10PercentEvery1Minute | 10%/分ずつ | — | 段階的移行優先 |
| LambdaLinear10PercentEvery2Minutes | 10%/2分ずつ | — | 慎重な移行 |
| LambdaAllAtOnce | 一括 100% | — | 開発環境・低リスク |
本番では LambdaCanary10Percent10Minutes を基本とし、高リスク変更時のみ 15Minutes に切替える運用を推奨する。
5-4. CloudWatch Alarm 連動 auto-rollback
auto-rollback が機能するためには CloudWatch Alarm が Alias ディメンション付きで設定されていること が必須だ。Resource ディメンションを FunctionName:AliasName 形式で指定しないと Alias トラフィック分のメトリクスが計上されず Alarm が発火しない。
resource "aws_cloudwatch_metric_alarm" "lambda_canary_errors" {
alarm_name = "lambda-my-function-LIVE-errors"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = "2"
metric_name= "Errors"
namespace = "AWS/Lambda"
period = "60"
statistic = "Sum"
threshold = "5"
treat_missing_data = "notBreaching"
dimensions = {
FunctionName = "my-production-function"
Resource = "my-production-function:LIVE" # Alias ディメンション必須
}
}
resource "aws_cloudwatch_metric_alarm" "lambda_canary_duration" {
alarm_name = "lambda-my-function-LIVE-duration"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = "3"
metric_name= "Duration"
namespace = "AWS/Lambda"
period = "60"
statistic = "Average"
threshold = "5000"
treat_missing_data = "notBreaching"
dimensions = {
FunctionName = "my-production-function"
Resource = "my-production-function:LIVE"
}
}
落とし穴: Resource ディメンションを function だけで設定すると Alias 経由トラフィックのメトリクスが別ディメンションに集計されるため Alarm が機能しない。FunctionName:AliasName 形式を必ず使うこと。
5-5. Terraform / AWS CLI / コンソール 3点セット
Terraform — CodeDeploy Lambda DeploymentGroup:
resource "aws_codedeploy_app" "lambda_app" {
name = "my-lambda-app"
compute_platform = "Lambda"
}
resource "aws_codedeploy_deployment_group" "lambda_dg" {
app_name= aws_codedeploy_app.lambda_app.name
deployment_group_name = "my-lambda-dg-production"
service_role_arn = aws_iam_role.codedeploy_lambda.arn
deployment_config_name = "CodeDeployDefault.LambdaCanary10Percent10Minutes"
auto_rollback_configuration {
enabled = true
events = ["DEPLOYMENT_FAILURE", "DEPLOYMENT_STOP_ON_ALARM"]
}
alarm_configuration {
alarms = [
aws_cloudwatch_metric_alarm.lambda_canary_errors.alarm_name,
aws_cloudwatch_metric_alarm.lambda_canary_duration.alarm_name,
]
enabled = true
}
deployment_style {
deployment_option = "WITH_TRAFFIC_CONTROL"
deployment_type= "BLUE_GREEN"
}
}
AWS CLI — デプロイ作成と状態確認:
# デプロイ作成 (S3 に appspec.yml 配置済みの場合)
aws deploy create-deployment \
--application-name my-lambda-app \
--deployment-group-name my-lambda-dg-production \
--deployment-config-name CodeDeployDefault.LambdaCanary10Percent10Minutes \
--s3-location bucket=my-artifact-bucket,bundleType=YAML,key=appspec.yml
# デプロイ状態確認
DEPLOY_ID=$(aws deploy list-deployments \
--application-name my-lambda-app \
--deployment-group-name my-lambda-dg-production \
--query 'deployments[0]' --output text)
aws deploy get-deployment --deployment-id "$DEPLOY_ID" \
--query 'deploymentInfo.{status:status,errorInfo:errorInformation}'
# 手動 rollback
aws deploy stop-deployment \
--deployment-id "$DEPLOY_ID" \
--auto-rollback-enabled
コンソール操作:
- CodeDeploy → アプリケーション →
my-lambda-appを選択 - デプロイグループ →
my-lambda-dg-production→ デプロイの作成 - リビジョンの場所: S3 バケットパス (appspec.yml) を指定
- デプロイ設定:
LambdaCanary10Percent10Minutesを選択 - デプロイ開始後、イベント タブで hooks 実行状況とトラフィック shift 進捗をリアルタイム確認
- Alarm が ALARM 状態になると自動ロールバックが起動し LIVE Alias が旧バージョンに戻る
5-6. 落とし穴
| # | 落とし穴 | 症状 | 対策 |
|---|---|---|---|
| 1 | publish = true 未設定 | $LATEST のみ更新・バージョン番号不変 | Terraform に publish = true を必ず設定 |
| 2 | LIVE alias 未作成 | CodeDeploy がターゲット alias を見つけられずデプロイ失敗 | 初回 aws lambda create-alias --name LIVE で作成 |
| 3 | hooks 関数の権限漏れ | hook が 15 分タイムアウト → Failed | IAM に codedeploy:PutLifecycleEventHookExecutionStatus を付与 |
| 4 | Alarm の Resource ディメンション不正 | Alarm 不発 → auto-rollback 動作せず | FunctionName:LIVE 形式で設定 |
| 5 | appspec.yml の CurrentVersion/TargetVersion 固定値 | CI/CD で毎回手動修正が必要 | CI/CD パイプラインで動的に appspec.yml を生成しバージョン番号を注入 |
| 6 | rollback 後に LIVE alias が旧バージョン固定 | 次回デプロイが失敗または意図しない版がデプロイされる | rollback 後に aws lambda update-alias --name LIVE --function-version N で最新バージョンに更新してから再試行 |
6. Terraform完全実装 (Pipeline + Deploy + IAM + S3 + ECR 統合)
6-1. modular 構成設計
Vol2 (CodeBuild) の resource と連携しながら、役割ごとにファイルを分割する。CodeBuild project は data source で参照し重複定義を避けるのが modular 設計の要点だ。
pipeline/
├── variables.tf # 変数定義
├── main.tf# provider + backend
├── s3.tf # artifact store S3 (KMS 暗号化)
├── ecr.tf # ECR リポジトリ (ECS Blue/Green 用)
├── iam.tf # CodePipeline / CodeDeploy IAM Role
├── pipeline.tf # aws_codepipeline V2
├── codedeploy.tf# aws_codedeploy_app + deployment_group (ECS + Lambda)
└── outputs.tf# 出力値
# Vol2 (CodeBuild) からの data source 参照
data "aws_codebuild_project" "build" {
name = var.codebuild_project_name # Vol2 で作成済みの project 名
}
data "aws_caller_identity" "current" {}
6-2. variables.tf
variable "project_name" {
type = string
default = "my-pipeline"
}
variable "aws_region" {
type = string
default = "ap-northeast-1"
}
variable "environment" {
type = string
default = "production"
}
variable "github_connection_arn" {
type = string
description = "CodeStar Connections ARN for GitHub"
}
variable "github_repository" {
type = string
description = "GitHub owner/repo (例: myorg/myrepo)"
}
variable "github_branch" {
type = string
default = "main"
}
variable "codebuild_project_name" {
type = string
description = "Vol2 で作成済みの CodeBuild project 名"
}
variable "ecs_cluster_name" {
type = string
description = "ECS cluster 名 (ECS Blue/Green 用)"
}
variable "ecs_service_name" {
type = string
description = "ECS service 名 (ECS Blue/Green 用)"
}
variable "alb_listener_arn" {
type = string
description = "ALB 本番 Listener ARN"
}
variable "blue_target_group_name" {
type = string
description = "Blue Target Group 名"
}
variable "green_target_group_name" {
type = string
description = "Green Target Group 名"
}
variable "lambda_function_name" {
type = string
description = "Lambda function 名 (Canary デプロイ用)"
}
variable "alert_email" {
type = string
description = "デプロイ通知先 Email"
}
6-3. s3.tf / ecr.tf
# s3.tf — pipeline artifact store (KMS 暗号化)
resource "aws_s3_bucket" "artifact_store" {
bucket = "${var.project_name}-artifacts-${data.aws_caller_identity.current.account_id}"
force_destroy = false
}
resource "aws_s3_bucket_versioning" "artifact_store" {
bucket = aws_s3_bucket.artifact_store.id
versioning_configuration { status = "Enabled" }
}
resource "aws_s3_bucket_server_side_encryption_configuration" "artifact_store" {
bucket = aws_s3_bucket.artifact_store.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "aws:kms"
}
}
}
resource "aws_s3_bucket_public_access_block" "artifact_store" {
bucket= aws_s3_bucket.artifact_store.id
block_public_acls = true
block_public_policy = true
ignore_public_acls= true
restrict_public_buckets = true
}
# ecr.tf — ECR リポジトリ (ECS Blue/Green 用コンテナイメージ)
resource "aws_ecr_repository" "app" {
name = "${var.project_name}-app"
image_tag_mutability = "IMMUTABLE"
image_scanning_configuration { scan_on_push = true }
encryption_configuration { encryption_type = "KMS" }
}
resource "aws_ecr_lifecycle_policy" "app" {
repository = aws_ecr_repository.app.name
policy = jsonencode({
rules = [{
rulePriority = 1
description = "Keep last 30 images"
selection = { tagStatus = "any", countType = "imageCountMoreThan", countNumber = 30 }
action = { type = "expire" }
}]
})
}
6-4. iam.tf
# CodePipeline IAM Role
resource "aws_iam_role" "codepipeline" {
name = "${var.project_name}-codepipeline-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{ Action = "sts:AssumeRole", Effect = "Allow",
Principal = { Service = "codepipeline.amazonaws.com" } }]
})
}
resource "aws_iam_role_policy" "codepipeline" {
name = "${var.project_name}-codepipeline-policy"
role = aws_iam_role.codepipeline.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect= "Allow"
Action= ["s3:GetObject", "s3:PutObject", "s3:GetBucketVersioning",
"s3:GetObjectVersion", "s3:ListBucket"]
Resource = [aws_s3_bucket.artifact_store.arn,
"${aws_s3_bucket.artifact_store.arn}/*"]
},
{
Effect= "Allow"
Action= ["codebuild:StartBuild", "codebuild:BatchGetBuilds"]
Resource = data.aws_codebuild_project.build.arn
},
{
Effect= "Allow"
Action= ["codedeploy:CreateDeployment", "codedeploy:GetDeployment",
"codedeploy:GetDeploymentConfig", "codedeploy:RegisterApplicationRevision",
"codedeploy:GetApplicationRevision"]
Resource = "*"
},
{
Effect = "Allow"
Action = ["iam:PassRole"]
Resource = [aws_iam_role.codedeploy_ecs.arn,
aws_iam_role.codedeploy_lambda.arn]
Condition = {
StringEqualsIfExists = {
"iam:PassedToService" = ["codedeploy.amazonaws.com"]
}
}
},
{
Effect= "Allow"
Action= ["codestar-connections:UseConnection"]
Resource = var.github_connection_arn
}
]
})
}
# CodeDeploy IAM Role (ECS Blue/Green 用)
resource "aws_iam_role" "codedeploy_ecs" {
name = "${var.project_name}-codedeploy-ecs-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{ Action = "sts:AssumeRole", Effect = "Allow",
Principal = { Service = "codedeploy.amazonaws.com" } }]
})
}
resource "aws_iam_role_policy_attachment" "codedeploy_ecs" {
role = aws_iam_role.codedeploy_ecs.name
policy_arn = "arn:aws:iam::aws:policy/AWSCodeDeployRoleForECS"
}
# CodeDeploy IAM Role (Lambda Canary 用)
resource "aws_iam_role" "codedeploy_lambda" {
name = "${var.project_name}-codedeploy-lambda-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{ Action = "sts:AssumeRole", Effect = "Allow",
Principal = { Service = "codedeploy.amazonaws.com" } }]
})
}
resource "aws_iam_role_policy_attachment" "codedeploy_lambda" {
role = aws_iam_role.codedeploy_lambda.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSCodeDeployRoleForLambda"
}
6-5. pipeline.tf (aws_codepipeline V2)
resource "aws_codepipeline" "main" {
name = "${var.project_name}-pipeline"
role_arn= aws_iam_role.codepipeline.arn
pipeline_type = "V2" # 並列実行/条件分岐/変数/Triggers を有効化
artifact_store {
type = "S3"
location = aws_s3_bucket.artifact_store.bucket
}
variable {
name = "ImageTag"
default_value = "latest"
description= "ECR image tag passed from Build stage"
}
trigger {
provider_type = "CodeStarSourceConnection"
git_configuration {
source_action_name = "Source"
push {
branches { includes = ["main", "release/*"] }
tags { includes = ["v*"] }
}
}
}
stage {
name = "Source"
action {
name = "Source"
category= "Source"
owner= "AWS"
provider= "CodeStarSourceConnection"
version = "1"
output_artifacts = ["source_output"]
configuration = {
ConnectionArn = var.github_connection_arn
FullRepositoryId = var.github_repository
BranchName = var.github_branch
OutputArtifactFormat = "CODE_ZIP"
}
}
}
stage {
name = "Build"
action {
name = "Build"
category= "Build"
owner= "AWS"
provider= "CodeBuild"
version = "1"
input_artifacts = ["source_output"]
output_artifacts = ["build_output"]
namespace = "BuildVars"
configuration = { ProjectName = data.aws_codebuild_project.build.name }
}
}
stage {
name = "Deploy"
action {
name= "DeployECS"
category = "Deploy"
owner = "AWS"
provider = "CodeDeployToECS"
version= "1"
input_artifacts = ["build_output"]
configuration = {
ApplicationName = aws_codedeploy_app.ecs_app.name
DeploymentGroupName= aws_codedeploy_deployment_group.ecs_dg.deployment_group_name
TaskDefinitionTemplateArtifact = "build_output"
AppSpecTemplateArtifact = "build_output"
Image1ArtifactName = "build_output"
Image1ContainerName= "IMAGE1_NAME"
}
}
}
}
6-6. codedeploy.tf
# ECS Blue/Green Application + DeploymentGroup
resource "aws_codedeploy_app" "ecs_app" {
name = "${var.project_name}-ecs-app"
compute_platform = "ECS"
}
resource "aws_codedeploy_deployment_group" "ecs_dg" {
app_name= aws_codedeploy_app.ecs_app.name
deployment_group_name = "${var.project_name}-ecs-dg"
service_role_arn = aws_iam_role.codedeploy_ecs.arn
deployment_config_name = "CodeDeployDefault.ECSCanary10Percent5Minutes"
auto_rollback_configuration {
enabled = true
events = ["DEPLOYMENT_FAILURE", "DEPLOYMENT_STOP_ON_ALARM"]
}
blue_green_deployment_config {
deployment_ready_option {
action_on_timeout = "CONTINUE_DEPLOYMENT"
}
terminate_blue_instances_on_deployment_success {
action= "TERMINATE"
termination_wait_time_in_minutes = 10
}
}
deployment_style {
deployment_option = "WITH_TRAFFIC_CONTROL"
deployment_type= "BLUE_GREEN"
}
ecs_service {
cluster_name = var.ecs_cluster_name
service_name = var.ecs_service_name
}
load_balancer_info {
target_group_pair_info {
prod_traffic_route { listener_arns = [var.alb_listener_arn] }
target_group { name = var.blue_target_group_name }
target_group { name = var.green_target_group_name }
}
}
}
# Lambda Canary Application + DeploymentGroup
resource "aws_codedeploy_app" "lambda_app" {
name = "${var.project_name}-lambda-app"
compute_platform = "Lambda"
}
resource "aws_codedeploy_deployment_group" "lambda_dg" {
app_name= aws_codedeploy_app.lambda_app.name
deployment_group_name = "${var.project_name}-lambda-dg"
service_role_arn = aws_iam_role.codedeploy_lambda.arn
deployment_config_name = "CodeDeployDefault.LambdaCanary10Percent10Minutes"
auto_rollback_configuration {
enabled = true
events = ["DEPLOYMENT_FAILURE", "DEPLOYMENT_STOP_ON_ALARM"]
}
alarm_configuration {
alarms = [
aws_cloudwatch_metric_alarm.lambda_canary_errors.alarm_name,
aws_cloudwatch_metric_alarm.lambda_canary_duration.alarm_name,
]
enabled = true
}
deployment_style {
deployment_option = "WITH_TRAFFIC_CONTROL"
deployment_type= "BLUE_GREEN"
}
}
6-7. outputs.tf
output "pipeline_arn" {
value = aws_codepipeline.main.arn
description = "CodePipeline ARN"
}
output "artifact_bucket_name" {
value = aws_s3_bucket.artifact_store.bucket
description = "Artifact S3 バケット名"
}
output "ecr_repository_url" {
value = aws_ecr_repository.app.repository_url
description = "ECR リポジトリ URL"
}
output "ecs_codedeploy_app_name" {
value = aws_codedeploy_app.ecs_app.name
description = "ECS 用 CodeDeploy アプリケーション名"
}
output "lambda_codedeploy_app_name" {
value = aws_codedeploy_app.lambda_app.name
description = "Lambda 用 CodeDeploy アプリケーション名"
}
6-8. Terraform / AWS CLI / コンソール 3点セット
Terraform — 初回適用:
cd pipeline/
terraform init
terraform plan \
-var="github_connection_arn=arn:aws:codestar-connections:ap-northeast-1:123456789012:connection/xxxxx" \
-var="github_repository=myorg/myrepo" \
-var="codebuild_project_name=my-codebuild-project" \
-var="ecs_cluster_name=my-cluster" \
-var="ecs_service_name=my-service" \
-var="alb_listener_arn=arn:aws:elasticloadbalancing:..." \
-var="blue_target_group_name=my-blue-tg" \
-var="green_target_group_name=my-green-tg" \
-var="lambda_function_name=my-production-function" \
-var="alert_email=ops@example.com"
terraform apply
AWS CLI — パイプライン状態確認:
# パイプライン最新実行の状態確認
aws codepipeline get-pipeline-state \
--name my-pipeline-pipeline \
--query "stageStates[].{Stage:stageName,Status:latestExecution.status}" \
--output table
# 変数を上書きしてパイプライン手動実行
aws codepipeline start-pipeline-execution \
--name my-pipeline-pipeline \
--variables name=ImageTag,value=v1.2.3
# CodeDeploy 全デプロイ一覧
aws deploy list-deployments \
--application-name my-pipeline-ecs-app \
--deployment-group-name my-pipeline-ecs-dg \
--query 'deployments'
コンソール操作:
- CodePipeline →
my-pipeline-pipeline→ 実行グラフで各 Stage の成功/失敗/実行中を視覚確認 - 変数 タブで
ImageTagの現在値を確認 (V2 の場合のみ表示) - 編集 → トリガー タブで branch/tag フィルタを確認
- CodeDeploy → デプロイ でトラフィック shift 進捗をリアルタイム確認
- CloudWatch → Alarm で ECS 5xx / Lambda Errors アラームの状態を確認
7. CloudWatch Alarm + SNS 通知 + 監査ログ

- CloudWatch Alarm を DeploymentGroup に設定しないとデプロイが auto-rollback しない —
alarm_configuration.enabled = trueと alarm 名を必ず設定 - SNS サブスクリプション未確認 → 障害時に誰にも通知が届かない最悪ケースを排除 — Email は
PendingConfirmationからConfirmedへの移行を必ず確認 - EventBridge の
detail-typeは大文字小文字・スペースまで完全一致が必要 —"CodeDeploy Deployment State-change Notification"を正確に記述 - CloudTrail: management events のみ有効化だと S3/Lambda API 記録が欠落 — Data Events を明示的に追加
- X-Ray: Lambda Canary hook の実行ロールに
xray:PutTraceSegments権限が必要 — 欠如するとフックがサイレント失敗し Canary 検証が機能しない
7-1. CloudWatch Alarm 設計 — auto-rollback の要
CodeDeploy の auto-rollback は CloudWatch Alarm のステータスが ALARM になった瞬間にデプロイをロールバックする仕組みだ。ECS Blue/Green でも Lambda Canary でも以下のアラーム設計が必須となる。
ECS Blue/Green 向けアラーム
| アラーム名 | メトリクス | 閾値 (例) | 説明 |
|---|---|---|---|
ecs-5xx-rate | ApplicationELB / HTTPCode_Target_5XX_Count | 1分間 ≥ 10 | ALB Target Group の 5xx エラー急増 |
ecs-cpu-high | ECS / CPUUtilization | 平均 ≥ 80% | 新タスク起動後の CPU スパイク検知 |
ecs-unhealthy-hosts | ApplicationELB / UnHealthyHostCount | ≥ 1 | Green Target Group のヘルスチェック失敗 |
Lambda Canary 向けアラーム
| アラーム名 | メトリクス | 閾値 (例) | 説明 |
|---|---|---|---|
lambda-errors | Lambda / Errors | 1分間 ≥ 5 | Canary 版の Lambda Errors 急増 |
lambda-duration | Lambda / Duration | P99 ≥ 3000ms | レイテンシ悪化検知 |
lambda-throttles | Lambda / Throttles | ≥ 1 | 同時実行数上限ヒット |
ignore_poll_alarm_failure の重要性
ignore_poll_alarm_failure = false (デフォルト) の場合、CodeDeploy が CloudWatch Alarm のステータスを取得できない場面 (権限不足・スロットリング) ではデプロイをロールバックせず続行する。本番環境では true に設定して「アラーム確認できないならロールバック」という安全側設計を採用する。
resource "aws_codedeploy_deployment_group" "ecs_blue_green" {
# ... (他フィールドは §4 参照)
alarm_configuration {
alarms = [
aws_cloudwatch_metric_alarm.ecs_5xx.name,
aws_cloudwatch_metric_alarm.ecs_unhealthy.name,
]
enabled = true
ignore_poll_alarm_failure = true
}
auto_rollback_configuration {
enabled = true
events = ["DEPLOYMENT_FAILURE", "DEPLOYMENT_STOP_ON_ALARM"]
}
}
resource "aws_cloudwatch_metric_alarm" "ecs_5xx" {
alarm_name = "ecs-5xx-rate-${var.env}"
comparison_operator = "GreaterThanOrEqualToThreshold"
evaluation_periods = 1
metric_name= "HTTPCode_Target_5XX_Count"
namespace = "AWS/ApplicationELB"
period = 60
statistic = "Sum"
threshold = 10
treat_missing_data = "notBreaching"
dimensions = {
TargetGroup = aws_lb_target_group.green.arn_suffix
LoadBalancer = aws_lb.main.arn_suffix
}
}
resource "aws_cloudwatch_metric_alarm" "ecs_unhealthy" {
alarm_name = "ecs-unhealthy-hosts-${var.env}"
comparison_operator = "GreaterThanOrEqualToThreshold"
evaluation_periods = 1
metric_name= "UnHealthyHostCount"
namespace = "AWS/ApplicationELB"
period = 60
statistic = "Maximum"
threshold = 1
treat_missing_data = "notBreaching"
dimensions = {
TargetGroup = aws_lb_target_group.green.arn_suffix
LoadBalancer = aws_lb.main.arn_suffix
}
}
resource "aws_cloudwatch_metric_alarm" "lambda_errors" {
alarm_name = "lambda-canary-errors-${var.env}"
comparison_operator = "GreaterThanOrEqualToThreshold"
evaluation_periods = 1
metric_name= "Errors"
namespace = "AWS/Lambda"
period = 60
statistic = "Sum"
threshold = 5
treat_missing_data = "notBreaching"
dimensions = {
FunctionName = aws_lambda_function.main.function_name
Resource = "${aws_lambda_function.main.function_name}:LIVE"
}
}
AWS CLI:
aws deploy get-deployment-group \
--application-name myapp-ecs \
--deployment-group-name myapp-ecs-dg \
--query "deploymentGroupInfo.alarmConfiguration"
aws cloudwatch describe-alarms \
--alarm-names "ecs-5xx-rate-prod" "ecs-unhealthy-hosts-prod" \
--query "MetricAlarms[].{Name:AlarmName,State:StateValue}"
コンソール: CodeDeploy → Deployment groups → 対象グループ → Edit → Advanced → Alarms でアラーム名と “Ignore alarm status” を確認
7-2. SNS 通知 — デプロイイベントを Slack/Email に転送
SNS はデプロイ状態変化の中継点となる。Slack 連携には Lambda → Incoming Webhook または AWS Chatbot を使用する。
resource "aws_sns_topic" "deploy_notify" {
name = "codedeploy-notify-${var.env}"
}
resource "aws_sns_topic_subscription" "email" {
topic_arn = aws_sns_topic.deploy_notify.arn
protocol = "email"
endpoint = var.alert_email
}
resource "aws_sns_topic_subscription" "slack_lambda" {
topic_arn = aws_sns_topic.deploy_notify.arn
protocol = "lambda"
endpoint = aws_lambda_function.slack_notifier.arn
}
resource "aws_lambda_permission" "sns_invoke_slack" {
statement_id = "AllowSNSInvoke"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.slack_notifier.function_name
principal = "sns.amazonaws.com"
source_arn = aws_sns_topic.deploy_notify.arn
}
AWS CLI:
aws sns list-subscriptions-by-topic \
--topic-arn arn:aws:sns:ap-northeast-1:123456789012:codedeploy-notify-prod \
--query "Subscriptions[].{Protocol:Protocol,Endpoint:Endpoint}"
aws sns publish \
--topic-arn arn:aws:sns:ap-northeast-1:123456789012:codedeploy-notify-prod \
--message "Deploy notification test" \
--subject "CodeDeploy Test"
コンソール: SNS → Topics → 対象 topic → Subscriptions タブで Status が Confirmed であることを確認。Email サブスクリプションは確認メールのリンクをクリックするまで PendingConfirmation 状態になる。
7-3. EventBridge — CodeDeploy/CodePipeline 状態変化イベント
EventBridge を使うと CodeDeploy の Deployment state や CodePipeline の Pipeline state を SNS へルーティングできる。
resource "aws_cloudwatch_event_rule" "codedeploy_state" {
name = "codedeploy-state-${var.env}"
description = "CodeDeploy deployment state changes"
event_pattern = jsonencode({
source= ["aws.codedeploy"]
detail-type = ["CodeDeploy Deployment State-change Notification"]
detail = {
state = ["SUCCESS", "FAILURE", "STOP"]
}
})
}
resource "aws_cloudwatch_event_target" "codedeploy_to_sns" {
rule= aws_cloudwatch_event_rule.codedeploy_state.name
target_id = "SendToSNS"
arn = aws_sns_topic.deploy_notify.arn
}
resource "aws_cloudwatch_event_rule" "pipeline_state" {
name = "pipeline-state-${var.env}"
description = "CodePipeline execution state changes"
event_pattern = jsonencode({
source= ["aws.codepipeline"]
detail-type = ["CodePipeline Pipeline Execution State Change"]
detail = {
state = ["FAILED", "SUCCEEDED", "CANCELED"]
}
})
}
resource "aws_cloudwatch_event_target" "pipeline_to_sns" {
rule= aws_cloudwatch_event_rule.pipeline_state.name
target_id = "SendToSNS"
arn = aws_sns_topic.deploy_notify.arn
}
resource "aws_sns_topic_policy" "allow_eventbridge" {
arn = aws_sns_topic.deploy_notify.arn
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = { Service = "events.amazonaws.com" }
Action = "SNS:Publish"
Resource = aws_sns_topic.deploy_notify.arn
}]
})
}
AWS CLI:
aws events list-rules --name-prefix codedeploy-state \
--query "Rules[].{Name:Name,State:State}"
aws events list-targets-by-rule --rule codedeploy-state-prod \
--query "Targets[].{Id:Id,Arn:Arn}"
コンソール: EventBridge → Rules → 対象ルール → Targets タブで SNS topic ARN が設定され State が Enabled であることを確認
7-4. CloudTrail — API 操作の監査ログ
CodePipeline/CodeDeploy の API 操作を CloudTrail に記録し S3 に永続化する。S3/Lambda の Data Events は明示的に有効化が必要だ。
resource "aws_cloudtrail" "codepipeline_audit" {
name = "codepipeline-audit-${var.env}"
s3_bucket_name = aws_s3_bucket.trail.id
include_global_service_events = true
is_multi_region_trail= false
enable_log_file_validation = true
cloud_watch_logs_group_arn = "${aws_cloudwatch_log_group.trail.arn}:*"
cloud_watch_logs_role_arn = aws_iam_role.cloudtrail_cw.arn
event_selector {
read_write_type = "All"
include_management_events = true
data_resource {
type= "AWS::S3::Object"
values = ["${aws_s3_bucket.artifact.arn}/"]
}
data_resource {
type= "AWS::Lambda::Function"
values = ["arn:aws:lambda"]
}
}
}
resource "aws_s3_bucket" "trail" {
bucket = "codepipeline-trail-${var.env}-${data.aws_caller_identity.current.account_id}"
force_destroy = false
}
resource "aws_s3_bucket_lifecycle_configuration" "trail" {
bucket = aws_s3_bucket.trail.id
rule {
id = "expire-old-logs"
status = "Enabled"
filter { prefix = "" }
expiration { days = 365 }
}
}
AWS CLI:
aws cloudtrail get-trail --name codepipeline-audit-prod \
--query "Trail.{S3Bucket:S3BucketName,LogValidation:LogFileValidationEnabled}"
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=EventSource,AttributeValue=codedeploy.amazonaws.com \
--max-results 10 \
--query "Events[].{Time:EventTime,Event:EventName,User:Username}"
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=EventSource,AttributeValue=codepipeline.amazonaws.com \
--max-results 5 \
--query "Events[].{Time:EventTime,Event:EventName}"
コンソール: CloudTrail → Event history → Filter: Event source = codedeploy.amazonaws.com または codepipeline.amazonaws.com
7-5. X-Ray — Lambda Canary hooks トレース連携
Lambda Canary の hooks (BeforeAllowTraffic / AfterAllowTraffic) に X-Ray を有効化すると、Canary フェーズのレイテンシ分布や downstream 呼び出しを可視化できる。
resource "aws_lambda_function" "before_allow_traffic" {
function_name = "before-allow-traffic-${var.env}"
role = aws_iam_role.hook_lambda.arn
handler = "index.handler"
runtime = "python3.12"
filename= "hooks/before_allow_traffic.zip"
tracing_config {
mode = "Active"
}
}
resource "aws_lambda_function" "after_allow_traffic" {
function_name = "after-allow-traffic-${var.env}"
role = aws_iam_role.hook_lambda.arn
handler = "index.handler"
runtime = "python3.12"
filename= "hooks/after_allow_traffic.zip"
tracing_config {
mode = "Active"
}
}
resource "aws_iam_role_policy_attachment" "xray_hook" {
role = aws_iam_role.hook_lambda.name
policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
}
AWS CLI:
aws lambda get-function-configuration \
--function-name before-allow-traffic-prod \
--query "TracingConfig"
aws xray get-service-graph \
--start-time $(date -u -d '-1 hour' +%s) \
--end-time $(date -u +%s) \
--query "Services[].{Name:Name,Type:Type}"
コンソール: X-Ray → Service map → Lambda function (before-allow-traffic) をクリック → レイテンシ分布・エラー率を確認
8. まとめ + Vol4予告 + よくある落とし穴10選
- iam:PassRole 漏れ: CodePipeline 実行ロールに
iam:PassRole(CodeDeploy ロール宛) が欠けているとパイプラインが FAILED になる。Policy に"iam:PassRole"と Resource 指定を必ず追加。 - LIVE alias 未設定: Lambda Canary デプロイでは alias が必須。
$LATESTへの直接デプロイでは traffic shifting 不可。aws lambda create-alias --name LIVEでエイリアスを事前作成。 - ALB listener rule 重複: ECS Blue/Green で ALB に既存ルールがあると CodeDeploy がターゲットグループを切り替えられない。test/prod listener の port が競合していないか事前確認。
- auto-rollback alarm 未設定: DeploymentGroup の
alarm_configurationを設定しないと CloudWatch Alarm 悪化でもロールバックが走らない。enabled=trueと対象アラーム名を必ず設定。 - appspec.yml 形式ミス: ECS 用と Lambda 用で appspec.yml のスキーマが異なる。ECS は
TaskDefinition/ContainerPort/LoadBalancerInfoが必須。Lambda はfunctionsセクションにcurrentVersion/targetVersionが必要。 - ECS タスク定義 ARN の revision 指定: CodeDeploy に渡す taskdef.json に revision 番号を含めると古い revision が使われる事故が発生する。
"taskDefinitionArn": "arn:...:task-definition/myapp"(revision なし) で記述する。 - Lambda $LATEST 使用: BeforeAllowTraffic hook が
$LATESTで動いているとデプロイのたびにバージョンが変わり hook の ARN が無効になる。必ず published version の ARN を DeploymentGroup に指定する。 - Terraform pipeline_type V1 のまま:
pipeline_type = "V1"では並列実行・条件分岐・変数が使えない。既存パイプラインを V2 に移行する場合はpipeline_type = "V2"に変更しterraform applyを実行すること。 - CodeDeploy event type 誤設定: EventBridge ルールの
event_patternで"detail-type": "CodeDeploy Deployment State-change Notification"を正確に指定しないと通知が届かない。大文字小文字・ハイフンに注意。 - target group draining 時間設定: ECS Blue/Green で
termination_wait_time_in_minutesが 0 の場合、古いタスクが即座に終了し in-flight リクエストが切断される。最低 5 分以上を推奨。
8-1. 本記事のまとめ
本記事では CodePipeline V2 + CodeDeploy を使った本番グレードの CI/CD パイプライン構築を Terraform で完走した。以下が達成した主要事項だ。
| 達成事項 | 詳細 |
|---|---|
| CodePipeline V2 完全活用 | 並列実行・条件分岐・変数・Triggers を本番設計に適用 |
| ECS Blue/Green デプロイ | ALB Target Group 切替 + CloudWatch Alarm auto-rollback |
| Lambda Canary リリース | 10%→段階増加 + BeforeAllowTraffic/AfterAllowTraffic hooks |
| Terraform 完全実装 | Vol2 (CodeBuild) resource 参照の modular 構成 |
| 監視・監査基盤 | CloudWatch Alarm + SNS + EventBridge + CloudTrail + X-Ray |
Vol1 (サービス選定) → Vol2 (CodeBuild) → Vol3 (本記事) と積み上げることで、AWS Code* ファミリーを活用した完全な CI/CD 基盤が完成する。
8-2. キー CLI コマンド チートシート
# --- CodePipeline ---
aws codepipeline list-pipelines
aws codepipeline get-pipeline-state --name myapp-pipeline \
--query "stageStates[].{Stage:stageName,Status:latestExecution.status}"
aws codepipeline start-pipeline-execution --name myapp-pipeline
aws codepipeline stop-pipeline-execution \
--pipeline-name myapp-pipeline \
--pipeline-execution-id <id> \
--abandon
# --- CodeDeploy ---
aws deploy list-deployments \
--application-name myapp-ecs \
--deployment-group-name myapp-ecs-dg
aws deploy get-deployment --deployment-id d-XXXXXXXXX \
--query "deploymentInfo.{Status:status,Rollback:rollbackInfo}"
# --- CloudWatch Alarm ---
aws cloudwatch describe-alarms --state-value ALARM \
--query "MetricAlarms[].{Name:AlarmName,State:StateValue}"
aws cloudwatch set-alarm-state \
--alarm-name ecs-5xx-rate-prod \
--state-value ALARM \
--state-reason "Test rollback trigger"
# --- CloudTrail ---
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=EventSource,AttributeValue=codedeploy.amazonaws.com \
--max-results 10 \
--query "Events[].{Time:EventTime,Event:EventName}"
# --- SNS ---
aws sns list-subscriptions-by-topic \
--topic-arn arn:aws:sns:ap-northeast-1:123456789012:codedeploy-notify-prod
8-3. 本番投入前チェックリスト
terraform apply 後・初回デプロイ前に以下を必ず確認する。
| # | チェック項目 | 確認コマンド |
|---|---|---|
| 1 | CodePipeline が V2 設定済み | aws codepipeline get-pipeline --name <name> --query "pipeline.pipelineType" |
| 2 | ECS Service の deployment_controller が CODE_DEPLOY | aws ecs describe-services --cluster <c> --services <s> --query "services[].deploymentController" |
| 3 | ALB Target Group が Blue/Green 2 セット存在する | aws elbv2 describe-target-groups --query "TargetGroups[].TargetGroupName" |
| 4 | Lambda の LIVE alias が作成済み | aws lambda list-aliases --function-name <fn> --query "Aliases[?Name=='LIVE']" |
| 5 | DeploymentGroup に Alarm が有効設定済み | aws deploy get-deployment-group --app-name <a> --deployment-group-name <dg> --query "deploymentGroupInfo.alarmConfiguration" |
| 6 | SNS サブスクリプションが Confirmed | aws sns list-subscriptions-by-topic --topic-arn <arn> |
| 7 | CloudTrail Trail が有効 | aws cloudtrail get-trail-status --name <trail> |
| 8 | CodePipeline IAM Role に iam:PassRole が含まれる | aws iam simulate-principal-policy --policy-source-arn <role> --action-names iam:PassRole --resource-arns <codedeploy-role> |
8-4. Vol3 → Vol4 への布石
Vol4 (CodeArtifact + CodeGuru) では以下の拡張を予定している。
| Vol4 テーマ | Vol3 との連携ポイント |
|---|---|
| CodeArtifact | Build Stage で npm/Maven/pip パッケージを CodeArtifact から取得。外部パッケージの可視性・ライセンス管理を強化 |
| CodeGuru Reviewer | Source→Build 間に CodeGuru Reviewer を挿入し、コードレビュー品質ゲートを追加 |
| CodeGuru Profiler | Lambda Canary hooks に Profiler エージェントを組み込み、Canary フェーズのコストプロファイルを可視化 |
Vol3 の Terraform モジュール構成 (§6) を維持したまま Vol4 リソースを別モジュールとして追加できるよう aws_codeartifact_domain / aws_codeartifact_repository を分離しておくと拡張性が高い。
8-5. 関連記事
| 記事 | URL |
|---|---|
| Vol1: AWS Code* 全体像+サービス選定 | https://www.watchittrend.com/aws-code-family-overview-selection/ |
| Vol2: CodeBuild 完全活用 | https://www.watchittrend.com/aws-codebuild-deep-dive/ |
| ECS Fargate CI/CD vol1 | https://www.watchittrend.com/ecs-fargate-codepipeline-ecr-rolling-deploy/ |
| ECS Fargate CI/CD vol2 | https://www.watchittrend.com/ecs-fargate-codepipeline-bluegreen-deploy/ |
Vol4: CodeArtifact + CodeGuru 補完運用 (シリーズ完結)