Compares deployed CloudFormation templates with locally synthesized CDK templates to detect drift, validate changes, and ensure consistency before deployment. Use when the user wants to compare CDK output with a deployed stack, check for infrastructure drift, run a pre-deployment validation, audit IAM or security changes, investigate a failing deployment, or perform a 'cdk diff'-style review. Triggered by phrases like 'compare templates', 'check for drift', 'cfn drift', 'stack comparison', 'infrastructure drift detection', 'safe to deploy', or 'what changed in my CDK stack'.
90%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
# 1. Retrieve the deployed template
aws cloudformation get-template \
--stack-name <stack-name> \
--region <region> \
--profile <profile> \
--query TemplateBody \
--output json > deployed.json
# 2. Synthesize the local CDK template
make synth
cp cdk.out/<stack-name>.template.json local.json
# 3. Compare structure
jq 'keys' deployed.json
jq 'keys' local.json
# 4. Compare resource counts
jq '.Resources | length' deployed.json
jq '.Resources | length' local.json
# 5. Find added/removed resource IDs
diff <(jq -r '.Resources | keys[]' deployed.json | sort) \
<(jq -r '.Resources | keys[]' local.json | sort)
# 6. Deep diff of a specific resource
diff <(jq '.Resources.<ResourceId>' deployed.json) \
<(jq '.Resources.<ResourceId>' local.json)# Check AWS credentials
aws sts get-caller-identity --profile <profile>
# → If this fails: verify AWS_PROFILE or --profile value before proceeding
# Confirm stack exists
aws cloudformation describe-stacks \
--stack-name <stack-name> --region <region> --profile <profile> \
--query 'Stacks[0].StackStatus'
# → If StackNotFoundException: check stack name and region
# Confirm CDK project synthesises cleanly
make synth
# → If synth fails: fix missing env vars in env-local.mk / env.mk before proceeding# Deployed template
aws cloudformation get-template \
--stack-name <stack-name> --region <region> --profile <profile> \
--query TemplateBody --output json > deployed.json
# → Validate: jq '.' deployed.json >/dev/null || echo "ERROR: invalid JSON"
# Local template
cp cdk.out/<stack-name>.template.json local.json
# → Validate: jq '.' local.json >/dev/null || echo "ERROR: invalid JSON"# 1. Structure (top-level keys)
diff <(jq 'keys' deployed.json) <(jq 'keys' local.json)
# 2. Resource count
echo "Deployed: $(jq '.Resources | length' deployed.json)"
echo "Local: $(jq '.Resources | length' local.json)"
# 3. Added / removed resources
comm -3 \
<(jq -r '.Resources | keys[]' deployed.json | sort) \
<(jq -r '.Resources | keys[]' local.json | sort)
# 4. Security — CDK Nag suppressions
diff \
<(jq '[.Resources[].Metadata."cdk_nag" // empty]' deployed.json) \
<(jq '[.Resources[].Metadata."cdk_nag" // empty]' local.json)
# 5. IAM roles and policies
diff \
<(jq '[.Resources | to_entries[] | select(.value.Type | startswith("AWS::IAM"))]' deployed.json) \
<(jq '[.Resources | to_entries[] | select(.value.Type | startswith("AWS::IAM"))]' local.json)Categorise each difference before reporting:
| Category | Examples | Action |
|---|---|---|
| Expected | Environmental tags, GitRef, stack-name in resource IDs | Auto-approve |
| Low risk | Display names, cosmetic metadata | Note and approve |
| Medium risk | Alarm thresholds, EventBridge schedules, Lambda config | Review and approve |
| High risk | IAM policies, encryption settings | Require explicit sign-off |
| Critical | CDK Nag suppressions, public access flags, resource removal | Block — get InfoSec/stakeholder approval |
# Timestamped directory for audit trail
BRANCH=$(git rev-parse --abbrev-ref HEAD)
DIR="cfn-compare-results/$(date +%Y-%m-%d-%H%M%S)_deployed-main_local-${BRANCH}"
mkdir -p "$DIR"
cp deployed.json "$DIR/"
cp local.json "$DIR/"
jq -r '.Resources | keys[]' deployed.json | sort > "$DIR/deployed-resources.txt"
jq -r '.Resources | keys[]' local.json | sort > "$DIR/local-resources.txt"
# Write report
cat > "$DIR/comparison-report.md" <<EOF
# CloudFormation Template Comparison
## Summary
- Deployed: <stack-name> ($(jq '.Resources|length' deployed.json) resources)
- Local: <stack-name> ($(jq '.Resources|length' local.json) resources)
- Status: ✅ Safe to deploy | ⚠️ Review required | ❌ Critical issues
## Differences
<!-- Populate from Step 3 output -->
## Recommendations
<!-- List required actions -->
## Deployment Decision
<!-- Approve | Reject | Conditional — reasoning -->
EOFUse CloudFormation template comparison when:
cdk deploy is failing or showing unexpected diffsSkip template comparison when:
Always categorize diffs by risk level before approving:
WHY: invalid JSON from AWS CLI or CDK synth causes cryptic jq errors that waste time debugging.
BAD: jq '.Resources' deployed.json → parse error: Expected separator between values at line 1, column 3.
GOOD: jq '.' deployed.json >/dev/null && echo "valid" || echo "INVALID" before any comparison.
WHY: 5000+ line diffs are unreadable and hide critical changes in noise.
BAD: diff deployed.json local.json → terminal flooded with irrelevant formatting differences.
GOOD: hierarchical comparison (Step 3): resource counts, added/removed IDs, then targeted deep diffs per resource.
WHY: unauthorized privilege escalation or resource exposure can occur through subtle IAM modifications.
BAD: diff shows IAM role trust policy changed → "looks fine, deploying" → security breach.
GOOD: extract IAM diff specifically (jq filter for AWS::IAM::*), document justification, get InfoSec approval before deploy.
WHY: if deployment goes wrong, you lose the evidence of what changed and can't rollback confidently.
BAD: run comparison in terminal, approve deploy, stack fails → no record of what was attempted.
GOOD: timestamped directory with deployed.json, local.json, diff report: an audit trail for incident investigation.
WHY: a suppression silently disables a security check, so the template stays green while the risk grows.
BAD: the cdk_nag metadata diff shows a new suppression and the change is waved through as cosmetic.
GOOD: treat it as Critical, record the reason and obtain InfoSec or stakeholder approval before deploying.
WHY: removing a resource risks data loss that cannot be undone by redeploying.
BAD: the added/removed list shows a missing bucket or table and the deployment proceeds.
GOOD: block the deployment and review the removal with stakeholders first.
WHY: a wrong profile, stack name or failing synth produces empty or misleading templates that look like drift.
BAD: run get-template straight away and compare whatever comes back.
GOOD: run aws sts get-caller-identity, describe-stacks and make synth first, and fix failures before comparing.
WHY: different account IDs and ARNs make every diff noisy and unreliable.
BAD: compare a stack deployed in one account against a template synthesised for another.
GOOD: compare like for like, within one account and region, using the same profile.
WHY: auto-approving everything hides the few changes that need sign-off, while blocking everything stalls routine deployments.
BAD: mark every difference "looks fine" after a quick scan.
GOOD: categorise each difference (Expected, Low, Medium, High, Critical) and apply the matching action from the risk table.
| Error | Cause | Fix |
|---|---|---|
Stack not found | Wrong name/region | Verify --stack-name, --region, --profile |
CDK synth failed | Missing env var | Check env-local.mk and env.mk |
jq: parse error | Invalid JSON from CLI | Use --output json and --query TemplateBody |
Diff > 5000 lines | Template too large | Switch to hierarchical comparison (Step 3) instead of line diff |
aws CLI — configured with appropriate profilejq — JSON query and transformationmake — CDK synthesis via make synthbash — shell scriptingdiff / comm — comparison utilitiesscripts/compare-cfn-templates.shreferences/compare-cfn-templates.md.gitlab-ci.yml validate-template stagepantheon-org/tekhne
c8416e9
Also appears in
last in sync Aug 28, 2026
last in sync Aug 28, 2026
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.