Skip to main content

Termination Protection

Prevent accidental deletion of critical CloudFormation stacks by enabling termination protection, requiring explicit disablement before stack destruction.

Overview

Termination protection is a CloudFormation safeguard that:

  • Prevents stack deletion via console, CLI, or API
  • Requires two-step process: disable protection, then delete
  • Applies to both manual and automated deletions
  • Does not prevent stack updates (only deletion)

Enabling Termination Protection

Via carlin Configuration

carlin.yml
stackName: MyProductionApp
terminationProtection: true
region: us-east-1
carlin deploy

Via Command-Line Flag

carlin deploy --termination-protection

For Specific Environments

carlin.production.yml
terminationProtection: true
carlin.staging.yml
terminationProtection: false # Allow easy teardown
# Production (protected)
carlin deploy --config carlin.production.yml

# Staging (not protected)
carlin deploy --config carlin.staging.yml

When to Enable

EnvironmentRecommendationRationale
Production✅ AlwaysProtect critical infrastructure
Staging⚠️ OptionalBalance safety vs. flexibility
Development❌ Usually noEnable rapid iteration
Preview/PR❌ NoTemporary stacks should be ephemeral

Disabling Protection

To Delete a Protected Stack

# Step 1: Disable protection
aws cloudformation update-termination-protection \
--stack-name my-app-production \
--no-enable-termination-protection

# Step 2: Delete stack
carlin deploy --destroy
# or
aws cloudformation delete-stack --stack-name my-app-production

Via carlin (Update Then Destroy)

# Update stack to disable protection
carlin deploy --no-termination-protection

# Then destroy
carlin deploy --destroy

Verifying Protection Status

# Check if protection is enabled
aws cloudformation describe-stacks \
--stack-name my-app-production \
--query 'Stacks[0].EnableTerminationProtection'

Output:

true

Common Scenarios

Protecting Base Stacks

carlin.yml
# Base stack should always be protected
stackName: base-infrastructure
terminationProtection: true
carlin deploy base-stack

Environment-Specific Protection

carlin.ts
export default {
stackName: 'MyApp',
terminationProtection: process.env.ENVIRONMENT === 'Production',
region: 'us-east-1',
};
# Production deploys with protection
ENVIRONMENT=Production carlin deploy

# Development deploys without protection
ENVIRONMENT=Development carlin deploy

CI/CD with Protected Stacks

# GitHub Actions
- name: Deploy to Production
run: carlin deploy --environment Production --termination-protection

- name: Deploy to Staging
run: carlin deploy --environment Staging # No protection flag

Limitations

What Protection PreventsWhat It Allows
Stack deletionStack updates (add/modify/remove resources)
Accidental destroy commandsResource replacement during updates
API/CLI delete operationsUpdating protection status itself
CloudFormation console deletionDrift detection and correction

Stack Update Behavior

Termination protection does not prevent:

  • Updating stack resources
  • Replacing resources (e.g., changing Lambda runtime)
  • Removing resources from template (deleted during update)
  • Stack rollback on failed updates

Example:

# Initial deployment (protected)
Resources:
MyBucket:
Type: AWS::S3::Bucket

# Update (removes bucket) - SUCCEEDS even with protection
Resources:
MyFunction:
Type: AWS::Lambda::Function

The bucket will be deleted during the update, despite termination protection.

Best Practices

  1. Enable for production stacks: Always protect stacks containing critical data or infrastructure
  2. Document protection status: Include in README or deployment docs
  3. Combine with resource retention: Use DeletionPolicy: Retain for critical resources
  4. Automate protection: Set via config files, not manual CLI flags
  5. Review before disabling: Require approval workflow for removing protection
  6. Monitor changes: Alert on termination protection status changes

Resource-Level Protection

For additional safety, use DeletionPolicy on individual resources:

cloudformation/template.yml
Resources:
CriticalDatabase:
Type: AWS::RDS::DBInstance
DeletionPolicy: Retain # Keep even if stack is deleted
Properties:
# ... database config

CriticalBucket:
Type: AWS::S3::Bucket
DeletionPolicy: Snapshot # Create snapshot before deletion
Properties:
# ... bucket config

Combined Protection:

  • Termination protection: Prevents stack deletion
  • DeletionPolicy: Retain: Prevents resource deletion even if stack is deleted

Troubleshooting

IssueCauseSolution
Cannot delete stackProtection enabledDisable protection first
Protection not appliedMissing flag or configVerify terminationProtection: true in config
Stack deleted despite protectionProtection disabled in prior updateAudit CloudFormation events for protection changes
Resource deleted during updateNo DeletionPolicy on resourceAdd DeletionPolicy: Retain to critical resources

Monitoring Protection Changes

Create a CloudWatch alarm for protection status changes:

{
"source": ["aws.cloudformation"],
"detail-type": ["CloudFormation Stack Status Change"],
"detail": {
"stack-name": ["my-app-production"],
"status-details": {
"termination-protection": ["DISABLED"]
}
}
}

Forward to SNS for immediate alerts.

Policy Enforcement

Use AWS Organizations SCPs to require termination protection:

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Action": "cloudformation:CreateStack",
"Resource": "*",
"Condition": {
"StringEquals": {
"cloudformation:StackPolicyUrl": "",
"aws:RequestedRegion": "us-east-1"
},
"StringNotEquals": {
"cloudformation:EnableTerminationProtection": "true"
}
}
}
]
}

Example: Multi-Environment Strategy

carlin.base.yml
# Shared config
stackName: MyApp
region: us-east-1
carlin.production.yml
terminationProtection: true
awsAccountId: '999999999999'
carlin.staging.yml
terminationProtection: false
awsAccountId: '111111111111'
carlin.development.yml
terminationProtection: false
awsAccountId: '111111111111'

Deployment Commands:

# Production (protected)
carlin deploy --config carlin.production.yml

# Staging (not protected, easy teardown)
carlin deploy --config carlin.staging.yml

# Development (not protected)
carlin deploy --config carlin.development.yml