CloudFormation Template Comparison Skill
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'.
npx skills add github:pantheon-org/tekhne --skill infrastructure--cfn--template-compareMindset
- Treat the deployed template as the source of truth for what is live, and the synthesised template as a claim about what will be live.
- Compare from the coarse to the fine: structure, counts, added/removed IDs, then targeted deep diffs.
- Classify every difference by risk before reporting; a diff that has not been classified has not been reviewed.
- Keep the evidence: a comparison that is not saved cannot support an audit or an incident review.
Quick Start
# 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)
Expected Workflow
Step 1: Preparation — verify prerequisites
# 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
Step 2: Retrieval
# 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"
Step 3: Hierarchical Analysis
# 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)
Step 4: Risk Assessment
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 |
Step 5: Save Artifacts
# 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 -->
EOF
Decision Framework
When to Use This Skill
Use CloudFormation template comparison when:
- Pre-deployment validation: Verify CDK changes match expectations before deploying to prod
- Drift detection: Investigate whether console changes have diverged from IaC
- Security audits: Check for unauthorized IAM policy modifications or CDK Nag suppression changes
- Deployment troubleshooting: Understand why
cdk deployis failing or showing unexpected diffs - Change review: Provide stakeholders with concrete before/after comparison for approval
When NOT to Use This Skill
Skip template comparison when:
- Initial stack deployment: No deployed template exists yet — synthesize and deploy directly
- Cross-account comparisons: Different account IDs/ARNs make diffs noisy and unreliable
- Frequently changing resources: Dynamic autoscaling groups, ephemeral Lambdas — accept constant drift
- CloudFormation managed entirely outside CDK: If stack wasn’t created via CDK, comparison won’t map correctly
Risk Assessment Strategy
Always categorize diffs by risk level before approving:
- Auto-approve (green): Environmental tags, GitRef, timestamps — expected variance
- Review (yellow): Config changes (alarms, schedules) — verify intent, then approve
- Block (red): IAM policies, CDK Nag suppressions, resource deletions — require explicit sign-off
Anti-Patterns
NEVER compare templates without verifying both sources are valid JSON first
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.
NEVER rely on line-by-line diff for large templates
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.
NEVER approve deployments with unexplained IAM policy changes
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.
NEVER skip saving comparison artifacts before deployment
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.
NEVER approve a new CDK Nag suppression without documented justification
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.
NEVER deploy when the comparison shows a removed resource without stakeholder review
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.
NEVER skip the prerequisite checks before retrieving templates
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.
NEVER compare templates across different accounts
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.
NEVER treat Expected differences and High-risk differences with the same action
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 Recovery
| 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 |
Required Tools
awsCLI — configured with appropriate profilejq— JSON query and transformationmake— CDK synthesis viamake synthbash— shell scriptingdiff/comm— comparison utilities
Common Scenarios
- Clean deployment: identical resource counts, only expected environmental differences → approve
- Drift detected: deployed threshold differs from local → revert console change or update CDK
- New CDK Nag suppression: requires documented justification and InfoSec approval
- Resource removal: block deployment, data-loss risk — review with stakeholders first
References
- Automated script:
scripts/compare-cfn-templates.sh - Real-world examples:
references/compare-cfn-templates.md - CI/CD integration:
.gitlab-ci.ymlvalidate-templatestage
Explicit NEVER/WHY/consequence triplets with side-by-side bad/good examples. Generic warnings score low — concrete failure modes with real impact (security, correctness, performance) score high.
Runtime validation via eval scenarios proving the skill actually changes agent behaviour. Requires evals/ directory, instructions.json, ≥3 scenarios, and ≥80% instruction coverage in summary.json.
Appropriate rigidity for the skill type: strict rules for critical/security skills, balanced steps for process skills, flexible options for tool skills. Mismatched calibration (too rigid or too loose) reduces score.
Expert-only knowledge the AI couldn't derive on its own — domain-specific patterns, production gotchas, decision frameworks, and non-obvious tool behaviour. High scores mean <5% redundancy with what the model already knows.
A clear philosophy plus numbered step-by-step workflows with explicit entry/exit points. Should answer: what is the core principle, how to execute it, and when (and when not) to apply it.
Rich activation keywords and trigger scenarios in the description so the skill fires when needed. Best descriptions read like an exhaustive list of the tasks and concepts that should invoke this skill.
Copy-paste-ready code, complete runnable examples, real file paths and commands. Pseudocode and abstract descriptions score low — concrete, executable content scores high.
SKILL.md as a <100-line navigation hub pointing to focused references/ files. Content should be lazy-loaded on demand, not front-loaded. Includes actionable 'When to Use' conditions per reference.
Proper frontmatter, single-task focus, activation keywords, and cross-harness portability. Skills must work across 40+ agentic harnesses without hardcoded paths or agent-specific references.
Expert-only knowledge the AI couldn't derive on its own — domain-specific patterns, production gotchas, decision frameworks, and non-obvious tool behaviour. High scores mean <5% redundancy with what the model already knows.
A clear philosophy plus numbered step-by-step workflows with explicit entry/exit points. Should answer: what is the core principle, how to execute it, and when (and when not) to apply it.
Explicit NEVER/WHY/consequence triplets with side-by-side bad/good examples. Generic warnings score low — concrete failure modes with real impact (security, correctness, performance) score high.
Proper frontmatter, single-task focus, activation keywords, and cross-harness portability. Skills must work across 40+ agentic harnesses without hardcoded paths or agent-specific references.
SKILL.md as a <100-line navigation hub pointing to focused references/ files. Content should be lazy-loaded on demand, not front-loaded. Includes actionable 'When to Use' conditions per reference.
Appropriate rigidity for the skill type: strict rules for critical/security skills, balanced steps for process skills, flexible options for tool skills. Mismatched calibration (too rigid or too loose) reduces score.
Rich activation keywords and trigger scenarios in the description so the skill fires when needed. Best descriptions read like an exhaustive list of the tasks and concepts that should invoke this skill.
Copy-paste-ready code, complete runnable examples, real file paths and commands. Pseudocode and abstract descriptions score low — concrete, executable content scores high.
Expert-only knowledge the AI couldn't derive on its own — domain-specific patterns, production gotchas, decision frameworks, and non-obvious tool behaviour. High scores mean <5% redundancy with what the model already knows.
A clear philosophy plus numbered step-by-step workflows with explicit entry/exit points. Should answer: what is the core principle, how to execute it, and when (and when not) to apply it.
Explicit NEVER/WHY/consequence triplets with side-by-side bad/good examples. Generic warnings score low — concrete failure modes with real impact (security, correctness, performance) score high.
Proper frontmatter, single-task focus, activation keywords, and cross-harness portability. Skills must work across 40+ agentic harnesses without hardcoded paths or agent-specific references.
SKILL.md as a <100-line navigation hub pointing to focused references/ files. Content should be lazy-loaded on demand, not front-loaded. Includes actionable 'When to Use' conditions per reference.
Appropriate rigidity for the skill type: strict rules for critical/security skills, balanced steps for process skills, flexible options for tool skills. Mismatched calibration (too rigid or too loose) reduces score.
Rich activation keywords and trigger scenarios in the description so the skill fires when needed. Best descriptions read like an exhaustive list of the tasks and concepts that should invoke this skill.
Copy-paste-ready code, complete runnable examples, real file paths and commands. Pseudocode and abstract descriptions score low — concrete, executable content scores high.
No eval scenarios available.
CloudFormation Template Comparison - Examples
This document provides practical examples of using the CloudFormation template comparison skill.
Example 1: Basic Pre-Deployment Check
Scenario
Developer wants to verify changes before deploying to ST environment.
User Request
"Compare my local template with the deployed example-monitoring-st stack"
Agent Response
I'll compare your local CDK template with the deployed stack in example-st.
[Retrieves deployed template from AWS]
[Synthesizes local template using make synth]
[Performs comparison]
Comparison Results:
- Both templates have 192 resources
- Resource counts match exactly
- Found 3 expected environmental differences:
1. Stack naming: example-monitoring-st vs example-monitoring-st-user
2. Delete tag: 'no' (deployed) vs 'daily' (local dev)
3. GitRef: Full hash vs short hash
Status: ✅ SAFE TO DEPLOY
All differences are expected environmental variations.
No functional or security changes detected.
Example 2: Security Review
Scenario
Developer added new CDK Nag suppression and wants security validation.
User Request
"I added a CDK Nag suppression for IAM5. Check if this is safe."
Agent Response
I'll review the CDK Nag suppression changes for security concerns.
[Compares CDK Nag metadata between templates]
Security Analysis:
NEW SUPPRESSION DETECTED:
- Resource: BackupLambdaRole
- Rule: AwsSolutions-IAM5 (wildcards in IAM policies)
- Reason provided: "Wildcard required for S3 prefix operations"
Evaluation:
⚠️ REQUIRES REVIEW
The suppression allows wildcard permissions. I've verified:
1. The policy is scoped to a specific S3 bucket
2. Actions are limited to s3:GetObject and s3:PutObject
3. Resource includes bucket prefix: arn:aws:s3:::backup-bucket/logs/*
Recommendation:
- Document this suppression in docs/security/cdk-nag-suppressions.md
- Get InfoSec team approval before deployment
- Consider using more specific resource paths if possible
Additional context:
- This follows the pattern used in LoggingLambdaRole (approved 2025-12-15)
- IAM policy limits blast radius to single bucket
- No privilege escalation risk
Example 3: Drift Detection
Scenario
Deployment is failing with “resource already exists” error. Suspect manual console changes.
User Request
"My deployment keeps failing. Check for drift between deployed and local."
Agent Response
I'll check for infrastructure drift that might be causing deployment failures.
[Retrieves both templates and performs deep comparison]
DRIFT DETECTED:
1. Alarm Threshold Mismatch:
Resource: FreeStorageSpaceBelow25PercentAlarm
- Deployed: Threshold=20
- Local: Threshold=25
Cause: Manual change in CloudWatch console on 2026-02-15
Impact: Deployment will attempt to update alarm, may cause flapping
2. Security Group Rule Addition:
Resource: ExampleAccessSecurityGroup
- Deployed: Has ingress rule from 10.0.0.0/8 (not in local template)
- Local: Only has rule from VPC CIDR
Cause: Manual security group modification
Impact: Deployment will remove this rule (potential connectivity issue)
Status: ❌ CRITICAL DRIFT DETECTED
Recommendations:
1. Revert manual alarm change or update local CDK code to match
2. Document the 10.0.0.0/8 ingress rule:
- If intentional: Add to CDK code
- If temporary: Remove from console before deployment
3. Consider using drift detection in CI/CD to prevent this
Next steps:
- Option A: Import console changes into CDK code
- Option B: Revert console changes and deploy from CDK
Example 4: Large-Scale Changes
Scenario
Refactoring CDK code, want to ensure no unintended infrastructure changes.
User Request
"I refactored the Lambda construct. Verify that only naming changed, not functionality."
Agent Response
I'll validate that your refactoring is infrastructure-neutral.
[Compares templates with focus on Lambda resources]
Lambda Function Comparison:
TicketBaseAnalyticsLambda:
- Handler: IDENTICAL (app.src.ticket_base_analytics_handler.handler)
- Runtime: IDENTICAL (python3.13)
- Timeout: IDENTICAL (300 seconds)
- Memory: IDENTICAL (512 MB)
- Environment Variables: IDENTICAL (12 variables)
- IAM Role Policies: IDENTICAL (3 policies)
- VPC Configuration: IDENTICAL (subnets, security groups)
- ✅ Logical ID changed (expected from refactoring)
TicketCapacityMonitoringLambda:
- Handler: IDENTICAL
- Runtime: IDENTICAL (python3.13)
- Timeout: IDENTICAL (180 seconds)
- Memory: IDENTICAL (256 MB)
- Environment Variables: IDENTICAL (8 variables)
- IAM Role Policies: IDENTICAL (2 policies)
- ✅ Logical ID changed (expected from refactoring)
EventBridge Rules:
- Schedules: IDENTICAL (both still cron(0 10 ? * MON-FRI *))
- Targets: IDENTICAL (same Lambda ARNs)
- ✅ Logical IDs changed (expected from refactoring)
Status: ✅ REFACTORING VALIDATED
All functional properties are identical. Only logical IDs changed,
which is expected when refactoring construct code. CloudFormation
will update references but won't replace resources.
Deployment Impact: Zero downtime, in-place updates only.
Example 5: Multi-Resource Security Audit
Scenario
InfoSec team requested comprehensive security comparison before production deployment.
User Request
"Generate a complete security audit comparing local and deployed templates."
Agent Response
I'll perform a comprehensive security audit of all template changes.
[Analyzes IAM, encryption, network, and compliance configurations]
SECURITY AUDIT REPORT
Generated: 2026-02-18 15:47:11
Scope: example-monitoring-st (deployed) vs local template
=== IAM POLICIES ===
No changes to IAM policies detected.
- All Lambda execution roles: UNCHANGED
- All IAM policies: UNCHANGED
- Cross-account trust relationships: UNCHANGED
✅ IAM: No security-relevant changes
=== ENCRYPTION ===
SNS Topic (exampleMonitoringTopic064E4467):
- Deployed: KmsMasterKeyId=null (no encryption)
- Local: KmsMasterKeyId=null (no encryption)
- Status: ⚠️ INCONSISTENT DOCUMENTATION
CDK Nag Suppressions:
- Deployed: Suppresses SNS2 + SNS3 (reason: "not required")
- Local: Suppresses only SNS3 (reason: "auto-applied with KMS")
Issue: Both templates lack encryption, but suppression reasoning differs.
Recommendation: Clarify encryption policy:
- If encryption required: Remove suppressions, enable KMS
- If not required: Standardize suppression reasoning
RDS Encryption:
- Deployed: StorageEncrypted=true, KmsKeyId=<managed-key>
- Local: StorageEncrypted=true, KmsKeyId=<managed-key>
✅ RDS: No changes
=== NETWORK SECURITY ===
Security Groups:
- All rules: UNCHANGED
- No new ingress/egress rules
- No changes to port ranges
- CIDR blocks: UNCHANGED
✅ Network: No security-relevant changes
=== COMPLIANCE ===
CDK Nag Suppressions Summary:
- Total suppressions (deployed): 47
- Total suppressions (local): 46
- New suppressions: 0
- Removed suppressions: 1 (SNS2 on exampleMonitoringTopic)
⚠️ Suppression change requires documentation
CloudWatch Logs:
- Retention: UNCHANGED (30 days)
- Encryption: UNCHANGED (AWS managed)
✅ Logging: Compliant
=== SUMMARY ===
Security Status: ⚠️ REVIEW REQUIRED
Critical Issues: 0
High Priority: 1 (SNS encryption policy)
Medium Priority: 0
Low Priority: 0
Deployment Recommendation: CONDITIONAL APPROVAL
Conditions for deployment:
1. Document SNS encryption policy decision in ADR
2. Standardize CDK Nag suppression reasoning
3. Get InfoSec sign-off on SNS policy
No blocking security issues detected. Infrastructure changes
are minimal and non-security-related (display name addition).
Example 6: Quick Status Check
Scenario
In the middle of development, quick check if current state is deployable.
User Request
"Quick check - can I deploy right now?"
Agent Response
I'll do a quick comparison to check deployment readiness.
[Performs fast structural comparison]
Quick Status Check:
✅ Template synthesizes successfully
✅ Resource count matches (192 resources)
✅ No resources added or removed
✅ No IAM policy changes
✅ No encryption setting changes
✅ Expected environmental differences only
Status: ✅ READY TO DEPLOY
Your current code is safe to deploy. All differences are
expected environmental variations (naming, tags).
To deploy:
make deploy
Need detailed comparison?
make compare-templates
Example 7: Alarm Threshold Validation
Scenario
Ops team requested lowering alarm thresholds. Verify only alarm changes, nothing else.
User Request
"I changed alarm thresholds to be more sensitive. Verify only alarms changed."
Agent Response
I'll verify that only CloudWatch alarm configurations changed.
[Extracts and compares all alarm resources]
CloudWatch Alarm Changes:
FreeStorageSpaceBelow30PercentAlarm:
- Deployed: Threshold=30
- Local: Threshold=35
- Change: +5 percentage points (LESS sensitive)
FreeStorageSpaceBelow25PercentAlarm:
- Deployed: Threshold=25
- Local: Threshold=30
- Change: +5 percentage points (LESS sensitive)
FreeStorageSpaceBelow15PercentAlarm:
- Deployed: Threshold=15
- Local: Threshold=20
- Change: +5 percentage points (LESS sensitive)
⚠️ ALERT: Thresholds were RAISED, not lowered
Other Changes:
- Alarm evaluation periods: UNCHANGED
- Alarm comparison operators: UNCHANGED
- SNS notification targets: UNCHANGED
- Alarm descriptions: UNCHANGED
Non-Alarm Resources:
✅ Lambda functions: UNCHANGED
✅ EventBridge rules: UNCHANGED
✅ IAM roles: UNCHANGED
✅ Security groups: UNCHANGED
✅ Database: UNCHANGED
Status: ⚠️ THRESHOLD DIRECTION MISMATCH
The alarm thresholds were raised (less sensitive), not lowered
(more sensitive) as intended. This is opposite of what was requested.
Recommendation: Review alarm changes before deployment.
- Current: Alarms trigger later (at worse conditions)
- Intended: Alarms trigger earlier (at better conditions)
Correct threshold values for "more sensitive":
- 30% alarm: Lower to 25%
- 25% alarm: Lower to 20%
- 15% alarm: Lower to 10%
Example 8: Resource Naming Validation
Scenario
Ensuring naming conventions are followed across all resources.
User Request
"Check if all resources follow our naming convention: <service>-<env>-<resource-type>"
Agent Response
I'll validate resource naming conventions across the template.
[Analyzes resource names and identifiers]
Resource Naming Audit:
SNS Topics:
✅ example-monitoring-topic-st-user (follows convention)
Lambda Functions:
✅ ticket-base-analytics-lambda-st-user
✅ ticket-capacity-monitoring-lambda-st-user
CloudWatch Alarms:
✅ example-monitoring-st-user-free-storage-below-30-percent
✅ example-monitoring-st-user-free-storage-below-25-percent
✅ example-monitoring-st-user-free-storage-below-15-percent
EventBridge Rules:
✅ ticket-base-analytics-daily-rule-st-user
✅ ticket-capacity-monitoring-daily-rule-st-user
Security Groups:
✅ example-access-sg-st-user
✅ example-db-sg-st-user
IAM Roles:
✅ ticket-base-analytics-lambda-role-st-user
✅ ticket-capacity-monitoring-lambda-role-st-user
Status: ✅ ALL RESOURCES COMPLY
All 192 resources follow the naming convention:
<service>-<resource>-<env>-<reference>
Convention compliance: 100%
Note: Logical IDs (internal to CloudFormation) may differ
from physical resource names, which is expected and acceptable.
Common Commands
# Basic comparison
./scripts/compare-cfn-templates.sh example-monitoring-st eu-west-1 example-st
# Specify local template explicitly
./scripts/compare-cfn-templates.sh example-monitoring-st eu-west-1 example-st cdk.out/my-stack.template.json
# Compare production stack
./scripts/compare-cfn-templates.sh example-monitoring-pr eu-west-1 example-prod
# Silent mode (exit codes only)
./scripts/compare-cfn-templates.sh example-monitoring-st eu-west-1 example-st --silent
Integration with Makefile
compare-templates: ## Compare local and deployed templates
@./scripts/compare-cfn-templates.sh $(LCT_MONITORING_STACK_NAME) $(AWS_REGION) $(AWS_PROFILE)
compare-pr: ## Compare with production stack
@./scripts/compare-cfn-templates.sh example-monitoring-pr eu-west-1 example-prod
Usage:
make compare-templates # Compare with current environment
make compare-pr # Compare with production
Tips for Effective Comparisons
- Always synthesize first: Run
make synthbefore comparing - Focus on intent: Describe what you changed, agent will verify
- Request specific checks: “Check IAM changes” vs general “compare templates”
- Use for pre-commit: Compare before committing infrastructure changes
- Automate in CI/CD: Add comparison step to pipeline
- Document differences: Maintain list of expected environmental variations
- Review security changes: Never skip review of IAM, encryption, or suppressions
Troubleshooting
“Stack not found”
- Verify stack name:
aws cloudformation list-stacks --region eu-west-1 --profile example-st - Check region and profile settings
“No template found in cdk.out”
- Run
make synthfirst - Check for CDK errors in synthesis
“jq: parse error”
- Ensure AWS CLI returns valid JSON
- Check AWS CLI version (should be >= 2.0)
“Multiple templates found”
- Specify template explicitly:
./scripts/compare-cfn-templates.sh stack region profile cdk.out/specific.template.json - Clean cdk.out:
rm -rf cdk.out && make synth
Example CloudFormation Comparison Report
This is an example of the markdown report generated by the compare-cfn-templates.sh script.
Sample Report Output
CloudFormation Template Comparison Report
Generated: 2026-02-18 15:30:45 UTC Deployed Stack: example-monitoring-st Region: eu-west-1 Profile: example-st Local Branch: feat-PROJ-990-clean-subscription-fix **DEPLOY_REFERENCE: user
Executive Summary
| Metric | Deployed | Local | Status |
|---|---|---|---|
| Total Resources | 55 | 55 | ✅ |
| Common Resources | 48 | 48 | ✅ |
| Added Resources | 0 | 7 | ⚠️ |
| Removed Resources | 7 | 0 | ⚠️ |
Template Structure
✅ Template structure matches - Both templates have identical top-level keys
Added Resources
The following 7 resource(s) exist in the local template but not in deployed:
- examplemonitoringstuserExampleAccessSecurityGroupfrom1… (
AWS::EC2::SecurityGroupIngress) - examplemonitoringstuserExampleAccessSecurityGroupfrom2… (
AWS::EC2::SecurityGroupIngress) - examplemonitoringstuserTicketBaseAnalyticsDailyRuleAllowEventRule… (
AWS::Lambda::Permission) - examplemonitoringstuserMonitoringMetadataDBFreeStorageSpace15Alarm (
AWS::CloudWatch::Alarm) - examplemonitoringstuserMonitoringMetadataDBFreeStorageSpace25Alarm (
AWS::CloudWatch::Alarm) - examplemonitoringstuserMonitoringMetadataDBFreeStorageSpace30Alarm (
AWS::CloudWatch::Alarm) - examplemonitoringstuserTaskSummaryAnalyticsDailyRuleAllowEventRule… (
AWS::Lambda::Permission)
Removed Resources
The following 7 resource(s) exist in the deployed template but not in local:
- examplemonitoringstExampleAccessSecurityGroupfrom1… (
AWS::EC2::SecurityGroupIngress) - examplemonitoringstExampleAccessSecurityGroupfrom2… (
AWS::EC2::SecurityGroupIngress) - examplemonitoringstTicketBaseAnalyticsDailyRuleAllowEventRule… (
AWS::Lambda::Permission) - examplemonitoringstMonitoringMetadataDBFreeStorageSpace15Alarm (
AWS::CloudWatch::Alarm) - examplemonitoringstMonitoringMetadataDBFreeStorageSpace25Alarm (
AWS::CloudWatch::Alarm) - examplemonitoringstMonitoringMetadataDBFreeStorageSpace30Alarm (
AWS::CloudWatch::Alarm) - examplemonitoringstTaskSummaryAnalyticsDailyRuleAllowEventRule… (
AWS::Lambda::Permission)
Security Analysis
CDK Nag Suppressions
| Environment | Resources with Suppressions |
|---|---|
| Deployed | 54 |
| Local | 54 |
✅ CDK Nag suppressions are consistent
Resource Type Breakdown
Deployed Stack Resource Types
- AWS::Lambda::Function: 12 resource(s)
- AWS::IAM::Role: 8 resource(s)
- AWS::RDS::DBInstance: 2 resource(s)
- AWS::CloudWatch::Alarm: 8 resource(s)
- AWS::EC2::SecurityGroup: 3 resource(s)
- AWS::Lambda::Permission: 6 resource(s)
- AWS::Events::Rule: 4 resource(s)
Local Template Resource Types
- AWS::Lambda::Function: 12 resource(s)
- AWS::IAM::Role: 8 resource(s)
- AWS::RDS::DBInstance: 2 resource(s)
- AWS::CloudWatch::Alarm: 8 resource(s)
- AWS::EC2::SecurityGroup: 3 resource(s)
- AWS::Lambda::Permission: 6 resource(s)
- AWS::Events::Rule: 4 resource(s)
Deployment Recommendation
⚠️ REVIEW REQUIRED
Templates have differences that require careful review before deployment.
Required Actions:
- Review all added and removed resources
- Verify changes are intentional
- Assess impact on running services
- Get stakeholder approval for significant changes
- Update documentation if needed
- Re-run comparison after addressing issues
Impact Assessment:
- Resource Changes: 7 added, 7 removed
- Security Impact: None
- Risk Level: MEDIUM (resources will be added)
Artifacts
The following files are available in this directory for detailed analysis:
deployed.json- Complete CloudFormation template from deployed stacklocal.json- Complete CloudFormation template from local synthesisdeployed-resources.txt- List of deployed resource logical IDslocal-resources.txt- List of local resource logical IDsdeployed-keys.txt- Deployed template top-level keyslocal-keys.txt- Local template top-level keys
Commands for Further Analysis
Compare specific resources
jq '.Resources["<ResourceLogicalId>"]' deployed.json
jq '.Resources["<ResourceLogicalId>"]' local.json
Find resources by type
jq -r '.Resources | to_entries[] | select(.value.Type == "AWS::Lambda::Function") | .key' local.json
Compare IAM policies
jq '.Resources | to_entries[] | select(.value.Type == "AWS::IAM::Role")' deployed.json > deployed-iam.json
jq '.Resources | to_entries[] | select(.value.Type == "AWS::IAM::Role")' local.json > local-iam.json
diff deployed-iam.json local-iam.json
Report Generated by: cfn-template-compare skill
Timestamp: 2026-02-18T15:30:45Z
Use Cases for Reports
1. Deployment Review Meetings
Share the markdown report with stakeholders before deployments to review changes and assess risks.
Example workflow:
- Generate report before scheduled deployment
- Share with tech lead and product owner
- Review differences in team meeting
- Approve or reject deployment based on findings
2. Change Management Tickets
Attach the report to change management tickets for approval workflows.
Example:
JIRA Ticket: INFRA-1234
Subject: Deploy LCT Monitoring Stack Updates
Attachment: 2026-02-18-153045_deployed-main_local-feat-PROJ-990_ref-user/comparison-report.md
Change Summary:
- 7 resources will be renamed due to DEPLOY_REFERENCE change
- No functional changes
- Security suppressions remain consistent
3. Audit Trail
Preserve reports as part of compliance and audit documentation showing infrastructure changes over time.
Example structure:
audit-artifacts/
2026-Q1/
2026-02-18-153045_deployed-main_local-feat-PROJ-990_ref-user/
2026-02-15-091230_deployed-main_local-main_ref-st/
2026-Q2/
...
4. Troubleshooting
Compare historical reports to identify when specific changes were introduced that may have caused issues.
Example: “When did the CloudWatch alarm threshold change from 80% to 90%?”
- Review historical comparison reports
- Find report showing threshold change
- Correlate with deployment date and incident timeline
5. Multi-Environment Comparison
Generate reports for ST, AT, and PR environments to ensure consistency across deployment pipeline.
Example workflow:
# Compare ST environment
bash compare-cfn-templates.sh example-monitoring-st eu-west-1 example-st
# Compare AT environment
bash compare-cfn-templates.sh example-monitoring-at eu-west-1 example-at
# Compare PR environment
bash compare-cfn-templates.sh example-monitoring-pr eu-west-1 example-pr
# Review all reports to ensure consistency
ls -l cfn-compare-results/
Report Interpretation Guide
Executive Summary Table
| Icon | Meaning |
|---|---|
| ✅ | No differences, safe |
| ⚠️ | Differences detected, review needed |
Deployment Recommendations
✅ SAFE TO DEPLOY:
- No functional differences detected
- Only environmental variations (tags, naming)
- Proceed with deployment
⚠️ REVIEW REQUIRED:
- Resource additions or removals detected
- Security configuration changes
- Requires stakeholder review before proceeding
Risk Levels
- LOW: Cosmetic changes, display names, tags
- MEDIUM: Resource additions, alarm threshold changes
- HIGH: Resource removals, IAM policy changes, encryption settings
Customizing Report Generation
Environment Variables
Control report generation behavior with environment variables:
# Disable artifact preservation (cleanup after comparison)
PRESERVE_ARTIFACTS=false bash compare-cfn-templates.sh example-monitoring-st eu-west-1 example-st
# Keep artifacts (default behavior)
PRESERVE_ARTIFACTS=true bash compare-cfn-templates.sh example-monitoring-st eu-west-1 example-st
Cleaning Old Reports
Manage disk space by removing old comparison reports:
# Keep only last 10 comparison runs
cd cfn-compare-results
ls -t | tail -n +11 | xargs rm -rf
# Remove reports older than 30 days
find cfn-compare-results -type d -mtime +30 -exec rm -rf {} +
# Remove all comparison artifacts
rm -rf cfn-compare-results/