Detect and Report CloudFormation Stack Drift
Run drift detection on a schedule, parse resource-level results, and alert on the specific resources changed outside the stack.
Task
Know which stack resources were modified outside CloudFormation before the next update silently reverts them.
When to use this
A support engineer changes a security group rule in the console to unblock a customer and never reverts it; the next stack update removes the rule and the customer breaks again.
Prerequisites
- AWS CLI v2 with CloudFormation permissions
- Stacks whose resource types support drift detection
- A scheduled job with an alert channel
Steps
1. Start drift detection for the stack and wait for it to finish.
aws cloudformation detect-stack-drift --stack-name payments-prod && aws cloudformation wait stack-drift-detection-complete --stack-name payments-prodExpected output: Detection starts and the waiter returns without an error once it completes.
2. Read the stack-level drift status after detection.
aws cloudformation describe-stacks --stack-name payments-prod --query 'Stacks[0].DriftInformation.StackDriftStatus' --output textExpected output: The status is DRIFTED when at least one resource differs and IN_SYNC otherwise.
3. List only the resources that were modified or deleted outside the stack.
aws cloudformation describe-stack-resource-drifts --stack-name payments-prod --stack-resource-drift-status-filters MODIFIED DELETED --query 'StackResourceDrifts[].{Logical:LogicalResourceId,Type:ResourceType,Status:StackResourceDriftStatus}' --output tableExpected output: A table names the changed security group and any deleted resource by logical ID.
4. Make the scheduled job alert only when the drift count is non-zero.
aws cloudformation describe-stack-resource-drifts --stack-name payments-prod --query 'length(StackResourceDrifts[?StackResourceDriftStatus!=`IN_SYNC`])' --output textExpected output: A non-zero count feeds the alert; zero keeps the nightly run silent.
Troubleshooting
Problem
A resource reports NOT_CHECKED
Solution
Nested stacks, custom resources, and some resource types are unsupported; read the status reason and track those resources separately.
Problem
Everything shows drift right after a deploy
Solution
Detection can run before the update settles; wait for UPDATE_COMPLETE and run detection again.
Limitations
- Drift detection compares declared template properties, not runtime configuration or data.
- Resources deleted from the template are no longer tracked, so their drift is invisible.
Console changes are invisible until the next deploy
CloudFormation records what it created, not what currently exists. When someone edits a security group or resizes an instance in the console, the stack believes the template is still reality, and the next update quietly restores the template. That is usually the right behavior, but it means an emergency change can be reverted in the middle of a release unless the team knows it exists. Drift detection compares each supported resource’s current properties against the template and reports what changed.
Run detection, then read it at resource level
Stack-level status is not enough to act on; the useful output is the per-resource drift list. Run detect-stack-drift, wait for completion, then query describe-stack-resource-drifts with a filter so the alert contains the logical IDs and property differences. Keep the detection in a scheduled job instead of on every deploy, because detection has a cost and a rate limit per account. Alert on a non-zero drifted count, and route the alert to the team that owns the stack.
aws cloudformation describe-stack-resource-drifts --stack-name payments-prod \
--stack-resource-drift-status-filters MODIFIED DELETED \
--query 'StackResourceDrifts[].{Logical:LogicalResourceId,Type:ResourceType,Status:StackResourceDriftStatus}' \
--output table
Close the loop
Every drift finding has two possible resolutions: codify the change in the template, or revert it. Do one of them within the same working day, or the finding becomes permanent noise. Add a comment to the stack template explaining intentional differences, and review detection coverage quarterly, since unsupported resource types silently report NOT_CHECKED. For nested stacks, run detection on each child stack, because parent-level detection does not descend reliably.