CloudFormation Hooks+Guardで月50件の設定ミスを自動検出。二階建て運用の現実的な実装記
セキュリティグループ全開放、暗号化忘れ…。手作業でのコンプライアンスチェックはもう限界。HooksとGuardを組み合わせた実装から失敗を含めた6ヶ月の運用知見まで。
CloudFormation Hooks・Guardを導入した背景
うちのチームで月20〜30件のCloudFormationスタック作成・更新をしてるんですが、去年の秋くらいから「あ、これセキュリティグループが全開放になってる」「RDSの暗号化忘れた」みたいな設定ミスがレビュー段階で引っかかるようになってました。最初は手作業でチェックリストを作ってたんですけど、正直マンパワーで対応するのは限界がある。そこで2026年2月くらいにCloudFormation HooksとGuardの本格導入を決めたんです。
今回はその6ヶ月の導入記を、失敗も含めて書きます。
CloudFormation HooksとGuardの違い
まず混乱しやすいんですが、HooksとGuardは別物です。
CloudFormation Hooksは、スタック作成・更新時にテンプレート検証をリアルタイムで実行するマネージドサービス。2024年から一般公開されて、2026年にはかなり安定してます。組織ポリシーベースで強制できるから、デプロイ段階での検証が確実。ただし公開ルールセットに限定されるのと、カスタムロジックは難しい。
CloudFormation Guardは、独立したオープンソースツール。Guard Rules Language(Ruby DSL)で自分たちのルールを書ける。ローカルで実行することもできるし、CI/CDパイプラインに組み込むのが簡単。ただし実行環境を自分で用意する必要があります。
正直なところ、どっちを選ぶか悩みました。うちはControl Towerで基盤構築してるので、最初はHooksだけで統制しようと思ってたんです。でも検証してみたら、Hooksの公開ルールセットだけだと業務要件を100%満たせない。結局、Hooksで基本的なセキュリティ・コンプライアンス检验をして、Guardでカスタムルールを書く—という二階建て運用に落ち着きました。
実装構成図
graph TB
subgraph Organization["AWS Organization"]
subgraph RootOU["Root OU"]
HooksPolicy["CloudFormation Hooks Policy"]
end
subgraph WorkloadOU["Workload OU"]
DevAcct["Dev Account"]
ProdAcct["Prod Account"]
end
end
subgraph DevPipeline["Dev Account CI/CD"]
GitRepo["GitHub"]
CodePipeline["CodePipeline"]
GuardLint["cfn-lint + Guard"]
CodeBuild["CodeBuild"]
end
subgraph CFExecution["CloudFormation Execution"]
Templates["CFn Templates"]
HooksValidation["CloudFormation Hooks<br/>Built-in Rules"]
CustomHooks["Custom Hooks<br/>Lambda"]
Deployment["Deploy/Update"]
end
subgraph Monitoring["Monitoring"]
SNS["SNS Notification"]
CloudWatch["CloudWatch Logs"]
SecurityHub["Security Hub"]
end
GitRepo -->|Push| CodePipeline
CodePipeline -->|Trigger| GuardLint
GuardLint -->|Pass/Fail| CodeBuild
CodeBuild -->|Deploy| Templates
Templates -->|Validation| HooksPolicy
HooksPolicy -->|Evaluate| HooksValidation
HooksValidation -->|Custom Rules| CustomHooks
CustomHooks -->|Result| Deployment
Deployment -->|Notify| SNS
Deployment -->|Logs| CloudWatch
CloudWatch -->|Aggregate| SecurityHub
style HooksPolicy fill:#ff9999
style GuardLint fill:#99ccff
style HooksValidation fill:#99ff99
style CustomHooks fill:#ffcc99
CloudFormation Hooksの実装—組織レベルの強制
まずHooksから始めました。Control TowerのLanding Zoneで基本的なセキュリティ設定はされてるんですが、CloudFormationレベルでもう一層の統制をかけたかった。
Hooksを有効化した流れは、AWS Organizations > Policies で CloudFormation Hooks Policy を作成して、Control Tower > Customizations で Hooks ルールを展開。そうすると各アカウントで自動的に Hooks が有効化される。
実装例として、うちが適用してるルールセット(2026年時点での公開ルール)がこちら:
| ルール | 内容 | 検出件数/月 |
|---|---|---|
AwsProposedModifications | 非推奨リソース・設定の警告 | 15〜20件 |
EncryptionEnabled | EBS・RDS・S3暗号化の必須化 | 8〜12件 |
IamPolicyBlacklist | 過度なIAM権限の検出 | 3〜5件 |
SecurityGroupIngressRestriction | セキュリティグループの0.0.0.0/0チェック | 10〜15件 |
RequiredTags | 必須タグの検証 | 5〜8件 |
で、実際に導入して気づいたことが、Hooksって「ブロック」か「警告」の二択しかないんです。「これは本当に重大」「これは推奨」みたいなグラデーションがない。だからHooksで全部をキャッチしようとするとFalse Positiveが増えまくって、チームから「毎回ルール変更してるじゃん」って不満が出た。
Hooksの設定例(AWS CLI):
aws cloudformation set-type-configuration \
--type HOOK \
--type-name AWS::CloudFormation::Hook::EncryptionEnabled \
--arn arn:aws:cloudformation:ap-northeast-1:123456789012:hook/arn:aws:cloudformation:ap-northeast-1::type/hook/AWS::CloudFormation::Hook::EncryptionEnabled \
--configuration "{\"EncryptionSettings\":{\"S3\":{\"Enabled\":true},\"RDS\":{\"Enabled\":true}}}" \
--region ap-northeast-1
実運用では、組織全体に ENFORCE / WARN を分けて適用しました。
{
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": "cloudformation:*",
"Resource": "*",
"Condition": {
"StringEquals": {
"aws:RequestedRegion": "ap-northeast-1"
}
}
},
{
"Effect": "Deny",
"Principal": "*",
"Action": "cloudformation:CreateStack",
"Resource": "*",
"Condition": {
"StringLike": {
"cloudformation:StackPolicyParameter": "*AllowPublicAccess*"
}
}
}
]
}
CloudFormation Guardの実装—カスタムルール化
Hooksだけだと満足度50%くらいだったので、Guardで業界ガイドラインとか内部基準を具体化しました。
Guardのセットアップは意外と簡単だった。ローカルマシンにcfn-guardをインストールして、テンプレートに対してルールを評価する流れです。
インストール:
cargo install cfn-guard
Guard Rulesの例(うちのチームが実装したやつ):
# S3バケットのブロックパブリックアクセス設定を必須化
rule s3_block_public_access_enabled {
resources.*[ type == 'AWS::S3::Bucket' ] {
properties.PublicAccessBlockConfiguration exists
properties.PublicAccessBlockConfiguration.BlockPublicAcls == true
properties.PublicAccessBlockConfiguration.BlockPublicPolicy == true
properties.PublicAccessBlockConfiguration.IgnorePublicAcls == true
properties.PublicAccessBlockConfiguration.RestrictPublicBuckets == true
}
}
# RDSインスタンスの暗号化を必須化
rule rds_encryption_enabled {
resources.*[ type == 'AWS::RDS::DBInstance' ] {
properties.StorageEncrypted == true
properties.KmsKeyId exists
}
}
# Lambda関数のReserved Concurrent Executionsを設定
rule lambda_reserved_concurrency {
resources.*[ type == 'AWS::Lambda::Function' ] {
properties.ReservedConcurrentExecutions exists
properties.ReservedConcurrentExecutions > 0
}
}
# VPCの有効なセキュリティグループ設定
rule vpc_security_group_no_public_ingress {
resources.*[ type == 'AWS::EC2::SecurityGroup' ] {
properties.SecurityGroupIngress[*] {
when CidrIp exists {
CidrIp != '0.0.0.0/0'
}
}
}
}
# CloudTrail有効化の確認
rule cloudtrail_enabled {
resources.*[ type == 'AWS::CloudTrail::Trail' ] {
properties.IsLogging == true
properties.S3BucketName exists
}
}
これらのルールをCI/CDパイプラインに統合してます。毎回CodeBuildでチェック。
CodeBuild buildspec.yml の例:
version: 0.2
phases:
install:
commands:
- echo "Installing cfn-guard and cfn-lint..."
- cargo install cfn-guard
- pip install cfn-lint
pre_build:
commands:
- echo "Running cfn-lint..."
- cfn-lint templates/*.yaml --format json | tee cfn-lint-output.json
- echo "Running CloudFormation Guard..."
- cfn-guard validate -t templates/*.yaml -r guard-rules/ --output json > guard-output.json
build:
commands:
- |
if [ $? -eq 0 ]; then
echo "All validations passed!"
else
echo "Validation failed!"
exit 1
fi
post_build:
commands:
- echo "Uploading validation reports..."
- aws s3 cp cfn-lint-output.json s3://audit-bucket/cfn-lint/
- aws s3 cp guard-output.json s3://audit-bucket/guard/
artifacts:
files:
- cfn-lint-output.json
- guard-output.json
これを運用してみて気づいたんですが、ルールを厳しくしすぎると開発者から恨まれます。最初、暗号化・タグ・ロギングは絶対ルールにしたんですが、フィーチャーブランチでの実験的なスタック作成に引っかかりすぎて。結果、本番スタック用と実験用の2つのルールセットに分けました。
# 本番用(厳しい)
cfn-guard validate -t templates/prod/*.yaml -r guard-rules/strict.guard
# 実験用(緩い)
cfn-guard validate -t templates/experimental/*.yaml -r guard-rules/relaxed.guard
検出実績と改善
運用6ヶ月で、Hooksとguardの組み合わせで月50〜60件の設定ミスを検出してます。実装前後で比較すると、かなり改善された。
xychart-beta
title CloudFormation デプロイ前のセキュリティ違反検出数
x-axis [1月, 2月, 3月, 4月, 5月, 6月]
y-axis "検出件数" 0 --> 80
line [0, 12, 38, 52, 58, 56]
line [0, 0, 0, 0, 0, 0] title "本番デプロイ後の違反(修正前)"
導入前だと本番デプロイ後にセキュリティ違反が発覚→緊急修正が月3〜5回。導入後はデプロイ前にキャッチできるから、本番デプロイ後の違反はほぼゼロになりました。
コスト面でも、デプロイ後の修正コスト(調査・対応・テスト・再デプロイ)が月平均8〜10時間削減されてます。
ハマった話と解決方法
1. False Positive地獄
最初、Guardのルールが多すぎてFalse Positiveが出まくった。「このテンプレートは意図的に全開放にしてるテスト用なんだけど…」みたいなケースがね。
解決はルールに除外条件を追加することでした。テンプレートのメタデータで「このスタックは検証を除外」とマークできるようにしたんです。
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Test template for security posture validation'
Metadata:
cfn-guard-exemption: |
rule: security_group_public_ingress_exempt
reason: "This is a test environment for staging"
approved-by: "security-team"
expires: "2026-12-31"
2. クロスアカウント参照の失敗
Hooksは評価対象テンプレート内のリソースしか見ないから、別アカウントのIAMロールやセキュリティグループを参照してる場合、検証失敗する。
解決は参照情報をテンプレートパラメータ化して、事前にチェックするようにしました。または、Guardで「外部参照の場合は検証スキップ」という条件を入れました。
3. 既存スタック互換性の問題
既存の200個以上のスタックが新しいルールに引っかかるんです。HooksをいきなりENFORCE設定にすると、スタック更新ができなくなる。地味に怖い。
解決は段階的なロールアウトです。最初はWARNでモニタリング。3ヶ月後にENFORCEに切り替え。その間に既存スタックを修正しました。
# 1ヶ月目: WARN で運用
aws cloudformation set-hook-status \
--hook-id arn:aws:cloudformation:ap-northeast-1::type/hook/AWS::CloudFormation::Hook::EncryptionEnabled \
--failure-mode WARN
# 4ヶ月目: ENFORCE に切り替え
aws cloudformation set-hook-status \
--hook-id arn:aws:cloudformation:ap-northeast-1::type/hook/AWS::CloudFormation::Hook::EncryptionEnabled \
--failure-mode ENFORCE
2026年のベストプラクティス
正直、HooksとGuardを完全に使い分けるのが今のベストプラクティスだと思う。Hooksで「絶対に守るべきルール」を組織レベルで統制。Guardで「チームや部門ごとのカスタムルール」を適用する。
そしてもう一つ重要なのが、Security Hub との統合。CloudFormation Hooks の実行結果をSecurity Hubに集約すると、コンプライアンス態勢がめちゃくちゃ見やすくなります。
# Security Hub統合
aws securityhub create-configuration-aggregator \
--region ap-northeast-1 \
--accounts {"Organization": {}}
これでSOC2とかの監査対応もかなり楽になりました。検証結果が自動的に記録されるから、「いつ・どのリソースが・何をチェックされたのか」が全部トレーサブルになります。
まとめ
-
CloudFormation Hooks は組織レベルの強制ツール。公開ルールセットで基本的なセキュリティ・コンプライアンスをガード。ただし柔軟性は低いのが課題。
-
CloudFormation Guard はカスタムルール化ツール。業務要件に合わせた細かいルールを書ける。CI/CDパイプラインへの統合も簡単で、実装の敷居も低い。
-
二階建て運用で現実的な落としどころに。Hooks で必須ルール、Guard で推奨ルール、という使い分けが実務的。
-
段階的なロールアウトは必須。いきなり ENFORCE すると既存スタックが死ぬ。WARN でモニタリング→修正→ENFORCE の流れは必ず守った方がいい。
-
False Positive との付き合い方が肝。除外条件やテンプレートメタデータで例外を管理しないと、開発者の信頼を失う。
設定ミスによる本番障害が月3〜5件から月0件になった。これだけでも導入価値があるかな。検証オーバーヘッドもビルド時間で+1〜2分程度。十分許容範囲です。
CloudTrail・Config監査設計で失敗した話。SOC2審査3ヶ月の地獄から学んだ実装パターン も合わせて読むと、IaC検証の全体像が見えやすいと思います。