CloudFormation Drift Detectionの全貌:構成管理の破綻を検知・修復する自動化テクニック
こんにちは。大規模クラウドインフラの設計・運用を統括しているテックリードだ。
インフラストラクチャー・アズ・コード(IaC)を導入している現場で、最も恐ろしい瞬間をご存知か?
それは、障害対応の緊急時に「ちょっとコンソールでセキュリティグループのインバウンドルールをいじった」結果、誰にも検知されず、次のデプロイで意図しないリソースの置き換えやロールバックが引き起こされる、いわゆる「構成のサイレント・ドリフト(漂流)」が発生している瞬間だ。
CloudFormationは宣言型の強力なツールだが、AWSマネジメントコンソールやAWS CLIを通じた「手動での直接変更(Out-of-band changes)」を防ぐ機能はデフォルトでは持っていない。IaCの聖域に人間が直接手を下した瞬間、コードと実態の乖離が始まり、インフラの信頼性は音を立てて崩れ去る。
今回は、このドリフトを完全に可視化し、検知から Slack へのアラート通知、そして修復に至るまでのワークフローを完全自動化する実践的テクニックを、プロダクション環境でそのまま使えるコードと共に余すところなく伝授しよう。
—
1. ドリフト検知のメカニズムと限界を知る
まず、CloudFormationの「ドリフト検出(Drift Detection)」が内部で何をしているのかを正確に理解する必要がある。
CloudFormationは、テンプレートに定義された「あるべき姿(Described State)」と、現在のAWSリソースの「実態(Actual State)」を比較する。検出結果は以下の3つのステータスに分類される。
- IN_SYNC: テンプレートの定義と実態が完全に一致している。
- MODIFIED: リソースのプロパティが手動変更などにより変更されている。
- DELETED: リソースがコンソール等から手動で削除されている。
⚠️ 本番運用における重大な落とし穴
ドリフト検出は万能ではない。以下の点に注意せよ。
1. サポート対象の制限: すべてのAWSリソースの全プロパティがドリフト検出をサポートしているわけではない(特に新機能やマイナーなプロパティは未対応の場合がある)。
2. コストとAPIスロットリング: 大規模スタックに対して高頻度でフル・ドリフト検出を実行すると、AWS CloudControl APIのレートリミットに抵触するリスクがある。
だからこそ、手動でポチポチとAWSコンソールから「ドリフト検出ボタン」を押すような運用は今すぐ捨て、イベント駆動型の完全自動化へと移行しなければならない。
—
2. 開発体験を最大化する:VS Code & CLI 活用術
自動化パイプラインに入る前に、開発者のローカル環境における「ドリフトを生み出さないための作法」と、開発スピードを劇的に高めるツールチェーンの設定を共有しておく。
神プラグイン:AWS Toolkit for Visual Studio Code
VS Codeを使っているなら、以下の拡張機能は必須だ。
- AWS Toolkit: CloudFormationテンプレートの構文チェックや、デプロイ済みスタックの状態確認をIDE内で完結させる。
- YAML by Red Hat: JSON Schema(CloudFormation用スキーマ)と連携させ、リソースプロパティのタイポをビルド前に根絶する。
チーム開発のための設定共有(`.vscode/settings.json`)
プロジェクトルートにこれを置くことで、チーム全員の記述ミスとコードフォーマットの揺れを防ぐ。
{
“editor.formatOnSave”: true,
“yaml.schemas”: {
“https://raw.githubusercontent.com/awslabs/goformation/master/schema/cloudformation.schema.json”: “.yaml”
},
“files.associations”: {
“.yaml”: “yaml”,
“template.yaml”: “yaml”
}
}
—
3. 実践:EventBridge + Lambda + SNS による「ドリフト自動検知・通知」パイプライン
ここからが本題だ。スタックのドリフトが発生した際、あるいは定期的にドリフト検出ジョブを走らせ、検知したら即座にSlack等へ通知するアーキテクチャを構築する。
全体のフロー:
1. Amazon EventBridge (Cron) が定期的に(例: 毎日深夜、あるいは1時間に1回)Lambdaをキック。
2. AWS Lambda が対象のCloudFormationスタックに対して `DetectStackDrift` APIを呼び出す。
3. ドリフトが検出された場合、EventBridge経由でイベントが発火、またはLambdaが結果をパースして Amazon SNS 経由でSlackへアラートを飛ばす。
構築用 CloudFormation テンプレート
以下のテンプレートをデプロイするだけで、指定したスタックのドリフトを定期監視する仕組みが完成する。
AWSTemplateFormatVersion: ‘2010-09-09’
Description: ‘Production Grade CloudFormation Drift Detection Automation Pipeline’
Parameters:
TargetStackName:
Type: String
Description: “The name of the CloudFormation stack to monitor for drift.”
Default: “core-network-stack”
SlackWebhookUrl:
Type: String
NoEcho: true
Description: “SNS or direct Lambda integration for Slack alerting (Placeholder).”
Resources:
# ==========================================
# 1. IAM Role for Drift Detection Lambda
# ==========================================
DriftDetectorLambdaRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: ‘2012-10-17’
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: DriftDetectionExecutionPolicy
PolicyDocument:
Version: ‘2012-10-17’
Statement:
# CloudFormationのドリフト検出権限
- Effect: Allow
Action:
- cloudformation:DetectStackDrift
- cloudformation:DescribeStackDriftDetectionStatus
- cloudformation:DescribeStackResourceDrifts
Resource: !Sub “arn:aws:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/${TargetStackName}/”
# Lambda基本ログ出力権限
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: “arn:aws:logs:::”
# ==========================================
# 2. Lambda Function (Drift Checker)
# ==========================================
DriftDetectorFunction:
Type: AWS::Lambda::Function
Properties:
Handler: index.lambda_handler
Role: !GetAtt DriftDetectorLambdaRole.Arn
Runtime: python3.9
Timeout: 60
Environment:
Variables:
STACK_NAME: !Ref TargetStackName
Code:
ZipFile: |
import os
import boto3
import time
cfn = boto3.client(‘cloudformation’)
stack_name = os.environ[‘STACK_NAME’]
def lambda_handler(event, context):
print(f”Starting drift detection for stack: {stack_name}”)
# ドリフト検出の非同期実行をリクエスト
response = cfn.detect_stack_drift(StackName=stack_name)
detection_id = response[‘StackDriftDetectionId’]
# 完了までポーリング (最大30秒)
status = ‘DETECTION_IN_PROGRESS’
while status == ‘DETECTION_IN_PROGRESS’:
time.sleep(3)
desc = cfn.describe_stack_drift_detection_status(StackDriftDetectionId=detection_id)
status = desc[‘DetectionStatus’]
print(f”Current Status: {status}”)
if status == ‘DETECTION_COMPLETE’:
drift_status = desc[‘StackDriftStatus’]
print(f”Drift Status Result: {drift_status}”)
if drift_status == ‘DRIFTED’:
# ドリフトしているリソースの詳細を取得
drifts = cfn.describe_stack_resource_drifts(
StackName=stack_name,
ResourceDriftStatusFilters=[‘MODIFIED’, ‘DELETED’]
)
# 実際のエラーメッセージ構築 (ここではログ出力&SNS連携の起点とする)
error_msg = f”[CRITICAL] Stack {stack_name} has drifted! Details: {drifts[‘StackResourceDrifts’]}”
print(error_msg)
# TODO: ここでSNSやSlackへのWebhook送信処理を実装する
raise Exception(error_msg)
else:
print(f”Stack {stack_name} is in sync. No drift detected.”)
else:
raise Exception(f”Drift detection failed with status: {status}”)
# ==========================================
# 3. EventBridge Rule (Scheduled Trigger)
# ==========================================
DriftCheckScheduleRule:
Type: AWS::Events::Rule
Properties:
Description: “Trigger CloudFormation drift detection every 6 hours”
ScheduleExpression: “rate(6 hours)”
State: ENABLED
Targets:
- Id: DriftDetectorTarget
Arn: !GetAtt DriftDetectorFunction.Arn
# ==========================================
# 4. Lambda Permission for EventBridge
# ==========================================
PermissionForEventsToInvokeLambda:
Type: AWS::Lambda::Permission
Properties:
FunctionName: !Ref DriftDetectorFunction
Action: “lambda:InvokeFunction”
Principal: “events.amazonaws.com”
SourceArn: !GetAtt DriftCheckScheduleRule.Arn
Outputs:
DriftDetectorFunctionName:
Description: “Lambda Function Name for Drift Detection”
Value: !Ref DriftDetectorFunction
—
4. ドリフト発生時の修復戦略:自動修復 vs 手動アプローチ
ドリフトが検知された場合、エンジニアが取るべきアクションは2つ存在する。
パターンA:手動でのリバースエンジニアリング(緊急時・非推奨)
コンソールで行われた変更が「正しい(正当な理由がある)」場合、その変更をCloudFormationテンプレート側に逆輸入し、コードを更新して `git commit` する。
注意: このアプローチを許容すると、「とりあえずコンソールで直して後でコードを直そう」という悪しき文化(ドリフトの常態化)が蔓延するため、原則として厳禁とすべきだ。
パターンB:IaCの絶対性を守る「強制上書き(強制再デプロイ)」(推奨)
ドリフトは「インフラストラクチャーに対する不正アクセスや意図しない変更」と見なし、CloudFormationのスタックを再適用(またはパイプラインによる強制デプロイ)することで、強制的に「あるべき姿(コード)」の状態へとリセットする。
パイプライン経由、あるいはCLIでの強制上書きデプロイ
aws cloudformation deploy \
–template-file template.yaml \
–stack-name core-network-stack \
–capabilities CAPABILITY_IAM \
–no-fail-on-empty-changeset
※ `MODIFIED` となったプロパティがテンプレートの定義値で強制的におこうみ直されるため、手動で行われたパッチなどは吹き飛ぶ。これこそが、IaCが目指すべき「イミュータブル・インフラストラクチャー」の思想である。
—
5. テックリードからの総括:組織としての規律をコードで縛る
CloudFormation Drift Detectionは、単なる「差分チェッカー」ではない。それは、「人による手動オペレーションの介在を許さない」という組織のガバナンスを機械的に強制するための強力な武器だ。
今回紹介した仕組みを導入することで、以下のメリットが確約される。
1. 構成管理の破綻の即時検知: サイレント・ドリフトによる次回デプロイ時の予期せぬ障害をゼロにする。
2. 監査対応の自動化: 「いつ、どのリソースが意図せず変更されたか」の証跡を自動で残せる。
3. エンジニアの心理的安全性の向上: 「誰かがコンソールを触ったかもしれない」という恐怖から解放され、コードベースだけでインフラを完全に信頼できるようになる。
今すぐあなたのプロダクション環境の主要なスタックにこの仕組みを組み込み、インフラの秩序を取り戻してほしい。妥協のないコードこそが、最高プロダクトの土台となるのだから。