
【Claude Code × AWS #1】CloudFormationレビューAIを作った——3サブエージェント構成と6つのデプロイ教訓
この記事では以下がわかる。
- 設計・生成・レビューの3サブエージェントが連携するCFnパイプラインの構成
- 実際に5スタック構成のVPC環境をデプロイして踏んだ6つのエラー
- エラーをSKILL.mdのルールとして書き込み、次回から防ぐ方法
スキル機能の基本的な仕組み(SKILL.mdの作り方・育て方)は前の記事で解説している。
作ったもの
/cfn-pipeline verification/test/ と打つだけで、ディレクトリ内のMarkdown要件ファイルを読み取り、CloudFormationテンプレートを順番に生成するスキル。
対象ファイル: 5件
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
ファイル [1/5]: verification/test/01_VPC_Subnet.md
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[1/3] 設計エージェント 実行中...
✓ [1/3] 設計エージェント 完了
## アーキテクチャ設計(確認)
...
この設計で進めてよいですか?
設計を承認すると、生成・lint・セキュリティスキャンが自動で走り、verification/test/cfn/01_VPC_Subnet.yaml に保存される。
3サブエージェントの役割分担
このスキルの中核は、オーケストレーターが3つのサブエージェントを順番に呼び出す構成。
オーケストレーター(SKILL.md本体)
↓
[1/3] 設計エージェント
要件ヒアリング → 不明点の確認 → アーキテクチャ設計書を出力
↓(ユーザーが設計を承認)
[2/3] 生成エージェント
CFnテンプレート生成 → cfn-lint → checkov
↓
[3/3] レビューエージェント
要件・設計書との照合 → approved / impl_issue / design_issue を判定
↓(approved の場合)
ファイルに保存 → 最終lint
設計エージェント
要件ファイルを受け取り、曖昧な点がある場合は生成前にユーザーに確認を求める。「対象環境はdev/stg/prodのどれか」「マルチAZ構成が必要か」「命名規則は何か」などが揃っていない場合は設計を止めて質問する。
確認が完了したら設計書を出力する。
### 作成するリソース一覧
| リソースタイプ | 論理ID | 用途 |
| ---------------- | ------------- | ------------------------- |
| AWS::EC2::VPC | VPC | メインVPC 10.0.0.0/16 |
| AWS::EC2::Subnet | SubnetPublicA | パブリックサブネット AZ-a |
...
### リソース間の依存関係
VPC → Subnet → RouteTable → Route
### パラメータ
| パラメータ名 | 型 | デフォルト値 |
| ------------ | ------ | ------------ |
| Env | String | prod |
設計書をユーザーに提示し、承認または修正指示を待ってから次のステップへ進む。
生成エージェント
設計書を受け取りYAMLテンプレートを生成する。生成後はcfn-lintでlintを実行し、エラーがあれば自動修正する。checkovでCRITICAL/HIGHのセキュリティ指摘を検出して修正まで行う。
レビューエージェント
生成されたテンプレートを「要件との照合」「設計書との照合」の2段階で検証し、結果をJSON形式で返す。
{
"route": "approved",
"summary": "要件・設計書との重大な不一致なし。lint 0件エラー。",
"requirements_check": [{ "item": "マルチAZ構成", "status": "ok" }],
"issues": []
}
route が impl_issue なら生成エージェントに修正を依頼、design_issue なら設計エージェントに戻す。最大3回のループで解決を試みる。
実際に動かした:5スタック構成のVPC環境
以下の要件ファイルを verification/test/ に置いて実行した。
| ファイル | 内容 |
|---|---|
| 01_VPC_Subnet.md | VPC・サブネット・IGW・NAT GW・FlowLogs |
| 03_SecurityGroup.md | SG×4(ALB/Web/AP/RDS)のSG間参照 |
| 04_ALB.md | ALB + TargetGroup + HTTPSリスナー |
| 05_EC2.md | EC2×4 + IAMロール + InstanceProfile |
| 06_RDS.md | RDS MySQL + ParameterGroup + LogGroups |
各ファイルで設計確認が入り、承認後に生成・レビューが走る。処理完了後は verification/test/cfn/ にYAMLが5本揃う。
踏んだ6つのエラーとルール化
生成されたテンプレートを実際にデプロイすると6つのエラーが出た。エラーごとに原因を分析し、SKILL.mdの生成エージェントプロンプトにルールとして追記した。
1. 非ASCII文字がコメントに混入した
VPCテンプレートのコメントに ─(U+2500、罫線文字)が入り込み、SSH Remote環境で「text contents could not be decoded」エラーが発生した。エージェントが見やすさのために罫線文字を使ったのが原因。
# 追記したルール
## 文字コード・エンコーディングルール
- YAMLコメントの区切りは `# ---` や `# ===` など ASCII文字のみを使う
- `─`(U+2500)等の罫線文字・全角文字をコメントに使わない
2. RDSエンジンバージョンが廃止済みだった
EngineVersion: "8.0.35" を指定したところ、ap-northeast-1では提供終了済みのバージョンだとしてデプロイに失敗した。エージェントが学習時点の情報をそのまま使い、実際の提供状況を確認しなかったのが原因。
# 追記したルール
## バージョン指定の事前検証ルール
要件にバージョンが明示されているリソースは、テンプレート生成前にAWS CLIで確認する。
| リソース | 確認コマンド |
|---|---|
| RDS (MySQL) | aws rds describe-db-engine-versions --engine mysql --engine-version <ver> --region <region> |
| EC2 AMI | aws ec2 describe-images --image-ids <ami-id> --region <region> |
| EC2インスタンスタイプ | aws ec2 describe-instance-type-offerings ... |
3. gp3 + Iops の組み合わせが失敗した
StorageType: gp3 に Iops: 3000 を明示したところ、AllocatedStorageが100GBしかないためエラーになった。gp3のデフォルト(3000 IOPS)を明示指定する場合、AWSはAllocatedStorage 400GB以上を要求する。
# 追記したルール
## RDS固有のルール
- StorageType: gp3 の場合、Iops のデフォルト値(3000)はわざわざ明示しない
- カスタム値を指定する場合は AllocatedStorage が400以上であることを確認する
4. デプロイ順序を間違えた
ALBスタックのデプロイ順序を 01→03→04→05→06 と案内していたが、ALBはターゲットグループにEC2のインスタンスIDをパラメータで受け取るため、EC2(05)を先にデプロイする必要があった。!ImportValue を使うクロススタック参照だけを依存として判断し、パラメータ経由の依存を見落とした。
# 追記したルール
## 前提条件・依存関係のルール
- デプロイ順序は !ImportValue だけでなく、パラメータ経由の依存関係も考慮する
例: ALBがEC2のインスタンスIDをパラメータで必要とする → EC2を先にデプロイ
5. IAM capabilities を指定し忘れた
EC2スタックに AWS::IAM::Role が含まれていたが、デプロイ時に --capabilities CAPABILITY_NAMED_IAM を指定し忘れてエラーになった。
# 追記したルール
## IAM capabilities のルール
IAMリソースが含まれる場合は、テンプレート冒頭のコメントに明記する。
# デプロイ時の注意:
# IAMリソースを含むため --capabilities CAPABILITY_NAMED_IAM が必要
6. DBパスワードが平文パラメータだった
RDSの MasterUserPassword を NoEcho: true のパラメータで受け取っていたが、CloudFormationのイベント履歴に平文が残るリスクがある。
# 追記したルール
## パスワード・シークレットのルール
NoEcho パラメータでパスワードを渡す場合、Secrets Manager の動的参照を推奨する旨をコメントに明記する。
# セキュリティ推奨:
# MasterUserPassword は Secrets Manager の動的参照を推奨
# 例: '{{resolve:secretsmanager:MySecret:SecretString:password}}'
エラー→ルール化のサイクル
今回のポイントは、エラーを踏むたびにSKILL.mdにルールを追記していったこと。
デプロイしてエラーが出る
↓
原因を分析する
↓
SKILL.mdの生成エージェントプロンプトにルールを追加する
↓
次回から同じエラーは生成前に防がれる
一般的なコードであれば「コードを修正する」だけで終わる。AIが生成する場合は「生成AIへの指示を修正する」という一段メタなレイヤーでの修正になる。このサイクルを回すことで、スキルが自分たちのチームのベストプラクティスを埋め込んだCFnジェネレーターになっていく。