はじめに
本記事は、本番運用を想定したCloudFormation設計・運用の実践ノウハウを整理しました。
特に、こんな方に読んでほしいです:
- 本番運用を想定したシステムを初めてCloudFormation(以下CFn)で構築する方
- 既存システムをCFnベースに移行・再構築しようとしている方
※ 本記事では、必要に応じてAWS CLIの例も紹介しますが、あくまで設計・運用の考え方を主軸としています。CDKは扱わず、CloudFormationテンプレート運用にフォーカスします。
本番運用の視点でIaC運用
本番運用では、CFnテンプレート書き方を覚えるだけでは不十分で、以下の観点を検討する必要があります。
- 変更は常に差分レビュー(Change Set)を通し、安全に適用する
- 重要リソースの保護(Stack Policy/DeletionPolicy)
- 更新失敗時に復旧できる(continue-update-rollback)
- 環境・リージョン差異はテンプレートではなくパラメータ管理へ寄せる
- Lint/Policy/Testの品質担保を定常運用へ
設計原則:モジュール化・命名・タグ
スタック分割(モジュール化)の基本
責務境界でスタックを分けると、更新影響を局所化でき、チーム分担も容易になります。
- network:VPC/Subnets/IGW/NAT
- security:SecurityGroups/NACL
- data:RDS/DynamoDB/S3/KMS/Secrets
- app:ECS/Lambda/ALB/AutoScaling
- edge:CloudFront/S3 (Web Hosting) /Route 53
依存の向きは「下位レイヤ(network/security)→ data → app → edge」。
上位が下位のOutputs(IDやARN)を参照する設計にします。
スタック・リソースの命名規約(例)
# スタック命名
<system>-<env>-<region>-<component>-<module>
mysys-dev-an1-frontend-edge
mysys-prod-ue1-batch-app
# リソース命名
<system>-<env>-<region>-<component>-<module>-<resource>-<suffix>
mysys-dev-an1-frontend-edge-s3-webapp1
mysys-prod-ue1-batch-app-ecscluster-daily2
タグ規約(例)
- すべてのリソースに
system,env,region,owner,cost-center - 監査ポリシー(cfn-guard)で タグ必須をチェック
安全な更新:変更セット・削除保護・復旧
変更セット(Change Set)
- 原則:テンプレートの直接Updateは避け、変更セットを作成 → 差分レビュー → 承認後に実行
- Nested構成の場合、親スタックに対する変更セットで子の差分も確認可能(ただし読みづらくなるため分割設計で補う)
スタック更新時にリソース削除・危険更新から保護(Stack Policy)
- RDS/KMS/Route 53などの重要リソースを保護
- 誤ったDeleteや不可逆な属性更新をポリシーで拒否
スタックの誤削除からリソースを保護(DeletionPolicy/Termination Protection)
- DeletionPolicy: Retain:CFnのDeleteでもリソースを残す
- Termination Protection:本番スタックで誤削除防止として有効化
更新失敗時の復旧(Rollback)
- 更新失敗(
UPDATE_ROLLBACK_IN_PROGRESS等)時はcontinue-update-rollbackで復旧 - イベントから失敗の原因(権限不足/依存未解消/Immutable属性)を分析・修正 → 再実行
環境・リージョンの拡張性:パラメータ運用でカバー
-
パラメータファイルで
SystemName/Env/RegionCodeと差異(CIDR、AZ、サイズ、スケール)を管理 - テンプレート内の分岐は Mappings/Conditions を最小限に留め、可読性を維持
- 秘匿情報(DBパスワード等)はSSM/Secretsで参照し、テンプレート直書き禁止
品質担保:lint・ポリシー・検証
- cfn-lint:構文・型・リソースの静的検査
- cfn-guard:ポリシー準拠(タグ必須、暗号化必須など)
- taskcat:複数リージョン×複数パラメータセットでのテンプレート検証
ネットワークと多アカウントの境界設計メモ
- StackSets:組織横断で共通設定(監査タグ、ベースラインSGなど)を配布
- Cross-Account参照:Exportsの制約を理解する。場合によってはSSM共有やパラメータ受け渡しで解決
- DNS/証明書:Route 53+ACM(ALB/CloudFront)連携をCFnに組み込み、「証明書発行 → 検証 → 適用」を一貫化
安全な更新フロー(CLIの例)
- 変更セットを作成
aws cloudformation create-change-set \
--stack-name mysys-dev-an1-backend-app \
--change-set-name cs-$(date +%Y%m%d%H%M) \
--template-body file://path-to-file/backend-app.yaml \
--parameters file://path-to-file/dev-an1-backend-app.json
- 差分をレビュー
aws cloudformation describe-change-set \
--stack-name mysys-dev-an1-backend-app \
--change-set-name cs-202512071234
- 承認後に実行
aws cloudformation execute-change-set \
--stack-name mysys-dev-an1-backend-app \
--change-set-name cs-202512071234
- 失敗時の復旧
aws cloudformation continue-update-rollback \
--stack-name mysys-dev-an1-backend-app
これまで経験したトラブルと解決策をピックアップ
1) 誤ってスタックを削除してしまった → 削除が完了してしまった
注意点:
- Delete Stackは不可逆
対策:
- まず空のスタックを作る → 以降は常にUpdate(変更セット)でリソースを追加・削除することを徹底
- DeletionPolicy: Retain(DB/S3/Hosted Zoneなど)
- Termination Protectionを本番で必須化
2) 誤ってスタックを削除してしまった → リソースの削除保護によってDELETE_FAILED(スタック削除失敗)となった
注意点:
- DELETE_FAILEDからスタックを復旧できない(更新も不可)
- リソースを削除せずにまたスタックで管理したい
対策:
- DeletionPolicy: Retainを設定済みの場合、リソースの削除保護を解除 → 再度スタック削除 → インポートでスタックを再作成
-
DeletionPolicy: Retainを設定していなかった場合、リソースの削除保護を解除 → CLIでスタック削除(
--retain-resourcesオプション必須) → インポートでスタックを再作成
3) スタックAの変更セットに含まれるExportを使用するスタックBの変更セット作成が失敗
原因:
- 変更セットが未実行の場合はExportが未確定のため、対応するImportValueを含む変更セットが作成できない
対策:
- スタックAの変更セットを実行 → スタックBの変更セットを作成 の順番で進める
- 初回はスタックBに暫定パラメータを設定し、スタックA作成後にスタックBをImportValueへ更新
-
Conditionで「参照不可なら固定値」を選ぶ二段階移行 - スタックA, Bを統合する(最終手段)
4) セキュリティグループのGroupNameを間違えただけなのに
注意点:
- セキュリティグループの一部属性は更新不可、再作成が必要
対策:
- 再作成に伴い、セキュリティグループの適用先に影響がないかを要確認
5) 複数環境をテンプレートレベルで分けすぎたデグレ
原因:
- dev/stg/prod差異が拡散し、保守・差分比較が困難になってしまった
対策:
- 単一テンプレート+パラメータファイルで統一
- 条件分岐は最小限。どうしても分けたい場合のみテンプレート分割
- taskcatで複数環境の自動テストを組み込む
まとめ
要点
- 変更セット作成 → 差分レビュー → 承認 → 実行の運用を徹底
- 削除保護で万が一の誤削除を防止
- 環境差異はパラメータ管理に寄せ、テンプレート分岐は最小化
- 品質担保(lint/guard/taskcat/drift)を定常運用に
参考:テンプレート例
1) SGをExport→他スタックからImport
security.yaml(SGのExport)
AWSTemplateFormatVersion: '2010-09-09'
Description: security.yaml
Parameters:
SystemName: { Type: String }
Env: { Type: String }
RegionCode: { Type: String }
VpcId: { Type: AWS::EC2::VPC::Id }
Resources:
AppSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: !Sub '${SystemName}-${Env}-${RegionCode}-security-sg'
VpcId: !Ref VpcId
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: 0.0.0.0/0
Tags:
- Key: system
Value: !Ref SystemName
- Key: env
Value: !Ref Env
- Key: region
Value: !Ref RegionCode
Outputs:
AppSGId:
Value: !Ref AppSecurityGroup
Export:
Name: !Sub '${SystemName}-${Env}-${RegionCode}-security-sg-id'
app.yaml(Importして利用)
Parameters:
SystemName: { Type: String }
Env: { Type: String }
RegionCode: { Type: String }
SubnetIds:
Type: List<AWS::EC2::Subnet::Id>
Resources:
ALB:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: !Sub '${SystemName}-${Env}-${RegionCode}-app-alb'
Subnets: !Ref SubnetIds
SecurityGroups:
- !ImportValue
'Fn::Sub': '${SystemName}-${Env}-${RegionCode}-security-sg-id'
2) Stack Policy(DB削除防止)
stack-policy.json
{
"Statement": [
{
"Effect": "Deny",
"Action": "Update:Delete",
"Principal": "*",
"Resource": "LogicalResourceId/ProductionDatabase"
}
]
}
3) DeletionPolicy: Retain(RDS/DynamoDB/S3重要リソース)
Resources:
ProdDB:
Type: AWS::RDS::DBInstance
DeletionPolicy: Retain
Properties:
# 以下略
参考:環境別パラメータファイル例
params/dev-an1-common-app.json
[
{ "ParameterKey": "SystemName", "ParameterValue": "mysys" },
{ "ParameterKey": "Env", "ParameterValue": "dev" },
{ "ParameterKey": "RegionCode", "ParameterValue": "an1" },
{ "ParameterKey": "SubnetIds", "ParameterValue": "subnet-aaa,subnet-bbb" }
]
params/prod-an1-common-app.json
[
{ "ParameterKey": "SystemName", "ParameterValue": "mysys" },
{ "ParameterKey": "Env", "ParameterValue": "prod" },
{ "ParameterKey": "RegionCode", "ParameterValue": "an1" },
{ "ParameterKey": "SubnetIds", "ParameterValue": "subnet-xxx,subnet-yyy" }
]
おわりに
この記事は、本番運用を想定したCloudFormation設計・運用の実践ノウハウを整理しました。
IaCを使ってシステムを構築する文化が少しでも広まることに貢献できたら幸いです。
間違っている情報・古い情報も含まれているかもしれなないので、コメントいただきましたら確認・修正します。
他、感想なども含めコメント大歓迎です、長文読んでいただきありがとうございました。