AWS CodePipeline V2 CodeDeploy 本番運用 ECS Blue/Green Lambda Canary

目次

1. この記事について

fig01: CodePipeline V2 + CodeDeploy 全体アーキテクチャ

シリーズ: AWS Code* ファミリー徹底解説

  • Vol1: 全体像 + サービス選定 + GHA/ecspresso 比較 (公開済)
  • Vol2: CodeBuild 完全活用 — buildspec / VPC / Artifact / Lambda compute (公開済)
  • Vol3 (本記事): CodePipeline V2 + CodeDeploy 本番運用 — ECS Blue/Green + Lambda Canary
  • Vol4: CodeArtifact + CodeGuru 補完運用 (予告)
本記事で分かること — CodePipeline V2 + CodeDeploy 全体像

  • 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 点が実装できる状態になる。

  1. CodePipeline V2 パイプライン: Source (GitHub/CodeCommit) → Build (CodeBuild) → Test (並列) → Deploy (条件分岐) の stage を Terraform aws_codepipeline で定義し、Variables と Triggers で動的に制御する
  2. ECS Blue/Green デプロイ: aws_codedeploy_deployment_group で ALB Target Group の Blue→Green 切替を自動化し、CloudWatch Alarm 連動で失敗デプロイを自動ロールバックする
  3. 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分
§3CodePipeline V2 完全活用★★☆45分
§4ECS Blue/Green デプロイ★★★60分
§5Lambda Canary リリース★★★45分
§6Terraform 完全実装★★★60分
§7CloudWatch Alarm + 監査★★☆30分
§8まとめ + 落とし穴10選★☆☆15分

このシリーズの学習ロードマップ

Vol1: AWS Code* ファミリー全体像・サービス選定判断ツリー  ↓  どのサービスをどう使うか把握Vol2: CodeBuild 完全活用 (buildspec / VPC / キャッシュ / Lambda compute)  ↓  ビルド基盤を構築Vol3 (本記事): CodePipeline V2 + CodeDeploy 本番デプロイ  ↓  デプロイパイプラインを完成Vol4: CodeArtifact + CodeGuru (予定)  パッケージ管理と品質分析

関連記事へのリンク

前記事: Vol2 CodeBuild 完全活用

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/GreenLambda Canary
対象コンテナアプリ (ECS Fargate/EC2)Lambda 関数
トラフィック制御ALB Target Group 切替Alias ウェイト調整
検証方法CloudWatch Alarm + WaitTimeInMinuteshooks (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. 前提・環境・準備

fig02: Vol2(CodeBuild)→Vol3(Pipeline+Deploy) 連携アーキ + IAM 構成

2-1. 前提環境

本記事のハンズオンを完走するには以下の環境が必要だ。

項目要件備考
AWS アカウント管理者権限または以下のポリシーを持つ IAM ユーザー/ロールAdministratorAccess 推奨 (学習環境)
Terraform1.9.x 以上terraform version で確認
AWS CLIv2.xaws --version で確認
Docker24.x 以上ECR push に使用
Git任意GitHub/CodeCommit 接続時

必要 IAM 権限 (最小権限)

codepipeline:*codedeploy:*codebuild:*ecs:*lambda:*iam:PassRoleiam:CreateRoleiam:AttachRolePolicys3:*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 CodePipelineV2 (2024 GA)CI/CD パイプライン オーケストレーション
AWS CodeDeployECS / LambdaBlue/Green・Canary デプロイエンジン
AWS CodeBuild任意 (Vol2 参照)Build stage — Docker image build + ECR push
ECS Fargate + ALBBlue/Green デプロイ対象サービス
Lambda + AliasCanary デプロイ対象関数
Terraformaws provider 5.xIaC 全リソース定義
S3Pipeline artifact store
ECRDocker image registry
CloudWatch Alarmsauto-rollback トリガー
SNSデプロイ成功/失敗通知
EventBridgeCodeDeploy イベント → 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/pipelineterraform initterraform plan -out=tfplan# 適用terraform apply tfplan# 作成されたリソース確認terraform state list | grep -E "codepipeline|codedeploy"

2-5. IAM Role 設計

本ハンズオンでは以下の 3 つの IAM Role を作成する。各ロールの信頼ポリシーと権限を正しく設定しないと、パイプラインが途中で停止するトラブルが頻発する。

IAM Role信頼主体主な権限用途
CodePipelineServiceRolecodepipeline.amazonaws.comS3/CodeBuild/CodeDeploy/iam:PassRolePipeline 全体の実行ロール
CodeDeployServiceRolecodedeploy.amazonaws.comECS/Lambda/S3/CloudWatch/SNSCodeDeploy が各 AWS サービスを操作するロール
CodeBuildServiceRolecodebuild.amazonaws.comECR/S3/Logs/CodeBuildBuild 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:PassRoleResource: "*" にしておかないと、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 形式で解説する。

QG-2: V1 vs V2 比較マトリクス + 並列実行設計

機能V1V2本番活用ポイント
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 の移行は必ずメンテナンスウィンドウで実施すること。

fig03: CodePipeline V2 並列実行+条件分岐フロー

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.arntype = "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_failureresult = "ROLLBACK" を設定すると、デプロイ後に CloudWatch Alarm が ALARM 状態になった場合に CodeDeploy の auto-rollback が起動する。on_entryresult = "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 を指定して変数を exportaction {  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 applySource Action 認証エラーでトリガー不動作コンソールで OAuth 認可を完了してから apply

4. CodeDeploy ECS Blue/Green デプロイ完全構築

fig04: ECS Blue/Green traffic shifting タイミング図

QG-3: ECS Blue/Green traffic shifting 戦略

  • 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"
DeploymentGroupECS Service + ALB Listener の紐付けBlue/Green 設定を内包
DeploymentConfigtraffic shifting 方式の指定Canary / Linear / AllAtOnce
ALB Target Group (Blue)現行バージョンへのトラフィック本番 Listener (443) が向く
ALB Target Group (Green)新バージョンへのトラフィックデプロイ中に CodeDeploy が切替
appspec.yamlデプロイ手順定義TaskDefinition + LoadBalancerInfo
imageDetail.json新コンテナイメージの URICodeBuild が生成・CodePipeline が渡す
taskdef.jsonECS タスク定義 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.0Resources:  - TargetService:Type: AWS::ECS::ServiceProperties:  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[].nameappspec.yamlContainerName と完全一致させること。

CodePipeline の Deploy Stage では InputArtifactsBuildOutput を指定し、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_FAILUREECS タスク起動失敗・ヘルスチェック失敗タスク定義の cpu/memory 不足・IAM 権限不足を確認
DEPLOYMENT_STOP_ON_ALARMCloudWatch 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

コンソール操作手順:

  1. CodeDeploy → [アプリケーション作成] → コンピューティングプラットフォーム: Amazon ECS
  2. [デプロイグループ作成] → サービスロール: CodeDeployRole (AWSCodeDeployRoleForECS 付与済み)
  3. ECS クラスター・サービス名を選択
  4. ALB・本番リスナー (443)・テストリスナー (8443)・Target Group (Blue/Green) を設定
  5. デプロイ設定: CodeDeployDefault.ECSCanary10Percent5Minutes
  6. ロールバック → 「失敗時に自動ロールバック」有効化 → CloudWatch アラーム名を入力
  7. Blue/Green 設定 → 「元のバージョンの終了時間」: 10 分

4-7. 落とし穴

#落とし穴症状対策
1ALB Target Group が 1 つだけデプロイ失敗: No valid target groupsBlue/Green 各 1 セット必ず作成
2deployment_optionWITHOUT_TRAFFIC_CONTROLtraffic shifting なしで即時全切替WITH_TRAFFIC_CONTROL 必須
3termination_wait_time が 0ロールバック不能 (Blue タスクが即終了)最低 5-10 分確保
4appspec.yaml の ContainerName がタスク定義と不一致デプロイ失敗: container not foundtaskdef.json の containerDefinitions[].name と完全一致させる
5CodeDeploy IAM Role に ecs:DescribeServices が欠如サービス検出失敗AWS 管理ポリシー AWSCodeDeployRoleForECS を使用
6ECS Service の deployment_controllerECS のままBlue/Green 非対応エラーdeployment_controller { type = "CODE_DEPLOY" } に変更

5. CodeDeploy Lambda Canary リリース戦略

fig05: Lambda Canary hooks シーケンス図

QG-4: Lambda Canary hooks 設計フローLambda Canary デプロイの成否を分けるのは Alias 設計hooks 関数の品質 の2点だ。$LATEST を直接 traffic shift の対象にすることはできない。必ず LIVE のような固定 Alias を用意し、バージョン番号を明示管理せよ。BeforeAllowTraffic / AfterAllowTraffic の両 hooks が成功を返すまで CodeDeploy はトラフィック切替を進めない。CloudWatch Alarm は Alias ディメンション (FunctionName:LIVE) で設定しないと Canary 分のメトリクスが計上されず auto-rollback が機能しない点に注意。

  1. 新バージョン ($LATEST を publish → 明示バージョン番号) を作成
  2. CodeDeploy が DeploymentGroup を検知 → BeforeAllowTraffic hook を起動
  3. hook 関数がスモークテスト/DB接続確認を実施 → Succeeded 返却
  4. Canary 割合 (例: 10%) で LIVE Alias のトラフィックを新バージョンへ shift
  5. CloudWatch Alarm を監視 → エラー率上昇で auto-rollback、正常なら AfterAllowTraffic hook へ
  6. 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 = trueTerraform で必須省略すると $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.0Resources:  - MyFunction:Type: AWS::Lambda::FunctionProperties:  Name: "my-production-function"  Alias: "LIVE"  CurrentVersion: "1"  TargetVersion: "2"Hooks:  - BeforeAllowTraffic: "smoke-test-function"  - AfterAllowTraffic: "validation-function"

BeforeAllowTraffic hooks 実装例 — トラフィック切替前のスモークテスト:

import boto3import jsondef 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 待機推奨用途
LambdaCanary10Percent5Minutes10% → 100%5 分低頻度リクエスト API
LambdaCanary10Percent10Minutes10% → 100%10 分標準本番 API
LambdaCanary10Percent15Minutes10% → 100%15 分高リスク変更
LambdaLinear10PercentEvery1Minute10%/分ずつ段階的移行優先
LambdaLinear10PercentEvery2Minutes10%/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}'# 手動 rollbackaws deploy stop-deployment \  --deployment-id "$DEPLOY_ID" \  --auto-rollback-enabled

コンソール操作:

  1. CodeDeployアプリケーションmy-lambda-app を選択
  2. デプロイグループmy-lambda-dg-productionデプロイの作成
  3. リビジョンの場所: S3 バケットパス (appspec.yml) を指定
  4. デプロイ設定: LambdaCanary10Percent10Minutes を選択
  5. デプロイ開始後、イベント タブで hooks 実行状況とトラフィック shift 進捗をリアルタイム確認
  6. Alarm が ALARM 状態になると自動ロールバックが起動し LIVE Alias が旧バージョンに戻る

5-6. 落とし穴

#落とし穴症状対策
1publish = true 未設定$LATEST のみ更新・バージョン番号不変Terraform に publish = true を必ず設定
2LIVE alias 未作成CodeDeploy がターゲット alias を見つけられずデプロイ失敗初回 aws lambda create-alias --name LIVE で作成
3hooks 関数の権限漏れhook が 15 分タイムアウト → FailedIAM に codedeploy:PutLifecycleEventHookExecutionStatus を付与
4Alarm の Resource ディメンション不正Alarm 不発 → auto-rollback 動作せずFunctionName:LIVE 形式で設定
5appspec.yml の CurrentVersion/TargetVersion 固定値CI/CD で毎回手動修正が必要CI/CD パイプラインで動的に appspec.yml を生成しバージョン番号を注入
6rollback 後に 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 = 1description  = "Keep last 30 images"selection = { tagStatus = "any", countType = "imageCountMoreThan", countNumber = 30 }action = { type = "expire" } }]  })}

6-4. iam.tf

# CodePipeline IAM Roleresource "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 + DeploymentGroupresource "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 + DeploymentGroupresource "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 initterraform 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'

コンソール操作:

  1. CodePipelinemy-pipeline-pipeline → 実行グラフで各 Stage の成功/失敗/実行中を視覚確認
  2. 変数 タブで ImageTag の現在値を確認 (V2 の場合のみ表示)
  3. 編集トリガー タブで branch/tag フィルタを確認
  4. CodeDeployデプロイ でトラフィック shift 進捗をリアルタイム確認
  5. CloudWatchAlarm で ECS 5xx / Lambda Errors アラームの状態を確認

7. CloudWatch Alarm + SNS 通知 + 監査ログ

fig06: CloudWatch Alarm → SNS → auto-rollback フロー

QG-5: 監査・通知の落とし穴と本番運用チェックリスト (前半)

  • 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-rateApplicationELB / HTTPCode_Target_5XX_Count1分間 ≥ 10ALB Target Group の 5xx エラー急増
ecs-cpu-highECS / CPUUtilization平均 ≥ 80%新タスク起動後の CPU スパイク検知
ecs-unhealthy-hostsApplicationELB / UnHealthyHostCount≥ 1Green Target Group のヘルスチェック失敗

Lambda Canary 向けアラーム

アラーム名メトリクス閾値 (例)説明
lambda-errorsLambda / Errors1分間 ≥ 5Canary 版の Lambda Errors 急増
lambda-durationLambda / DurationP99 ≥ 3000msレイテンシ悪化検知
lambda-throttlesLambda / 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選

QG-5: よくある落とし穴10選 — 本番投入前の最終チェックリスト

  1. iam:PassRole 漏れ: CodePipeline 実行ロールに iam:PassRole (CodeDeploy ロール宛) が欠けているとパイプラインが FAILED になる。Policy に "iam:PassRole" と Resource 指定を必ず追加。
  2. LIVE alias 未設定: Lambda Canary デプロイでは alias が必須。$LATEST への直接デプロイでは traffic shifting 不可。aws lambda create-alias --name LIVE でエイリアスを事前作成。
  3. ALB listener rule 重複: ECS Blue/Green で ALB に既存ルールがあると CodeDeploy がターゲットグループを切り替えられない。test/prod listener の port が競合していないか事前確認。
  4. auto-rollback alarm 未設定: DeploymentGroup の alarm_configuration を設定しないと CloudWatch Alarm 悪化でもロールバックが走らない。enabled=true と対象アラーム名を必ず設定。
  5. appspec.yml 形式ミス: ECS 用と Lambda 用で appspec.yml のスキーマが異なる。ECS は TaskDefinition/ContainerPort/LoadBalancerInfo が必須。Lambda は functions セクションに currentVersion/targetVersion が必要。
  6. ECS タスク定義 ARN の revision 指定: CodeDeploy に渡す taskdef.json に revision 番号を含めると古い revision が使われる事故が発生する。"taskDefinitionArn": "arn:...:task-definition/myapp" (revision なし) で記述する。
  7. Lambda $LATEST 使用: BeforeAllowTraffic hook が $LATEST で動いているとデプロイのたびにバージョンが変わり hook の ARN が無効になる。必ず published version の ARN を DeploymentGroup に指定する。
  8. Terraform pipeline_type V1 のまま: pipeline_type = "V1" では並列実行・条件分岐・変数が使えない。既存パイプラインを V2 に移行する場合は pipeline_type = "V2" に変更し terraform apply を実行すること。
  9. CodeDeploy event type 誤設定: EventBridge ルールの event_pattern"detail-type": "CodeDeploy Deployment State-change Notification" を正確に指定しないと通知が届かない。大文字小文字・ハイフンに注意。
  10. 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-pipelinesaws codepipeline get-pipeline-state --name myapp-pipeline \  --query "stageStates[].{Stage:stageName,Status:latestExecution.status}"aws codepipeline start-pipeline-execution --name myapp-pipelineaws 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-dgaws 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 後・初回デプロイ前に以下を必ず確認する。

#チェック項目確認コマンド
1CodePipeline が V2 設定済みaws codepipeline get-pipeline --name <name> --query "pipeline.pipelineType"
2ECS Service の deployment_controller が CODE_DEPLOYaws ecs describe-services --cluster <c> --services <s> --query "services[].deploymentController"
3ALB Target Group が Blue/Green 2 セット存在するaws elbv2 describe-target-groups --query "TargetGroups[].TargetGroupName"
4Lambda の LIVE alias が作成済みaws lambda list-aliases --function-name <fn> --query "Aliases[?Name=='LIVE']"
5DeploymentGroup に Alarm が有効設定済みaws deploy get-deployment-group --app-name <a> --deployment-group-name <dg> --query "deploymentGroupInfo.alarmConfiguration"
6SNS サブスクリプションが Confirmedaws sns list-subscriptions-by-topic --topic-arn <arn>
7CloudTrail Trail が有効aws cloudtrail get-trail-status --name <trail>
8CodePipeline 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 との連携ポイント
CodeArtifactBuild Stage で npm/Maven/pip パッケージを CodeArtifact から取得。外部パッケージの可視性・ライセンス管理を強化
CodeGuru ReviewerSource→Build 間に CodeGuru Reviewer を挿入し、コードレビュー品質ゲートを追加
CodeGuru ProfilerLambda 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 vol1https://www.watchittrend.com/ecs-fargate-codepipeline-ecr-rolling-deploy/
ECS Fargate CI/CD vol2https://www.watchittrend.com/ecs-fargate-codepipeline-bluegreen-deploy/

Vol4: CodeArtifact + CodeGuru 補完運用 (シリーズ完結)