โ๏ธ
CloudFormation
AWS CloudFormation: stacks, changesets, drift, StackSets, and template authoring
Stack Lifecycle
Create, update, delete, and describe stacks
bashยทCreate a stack
# Create from a local template
aws cloudformation create-stack \
--stack-name my-stack \
--template-body file://template.yaml \
--capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM
# Create from S3
aws cloudformation create-stack \
--stack-name my-stack \
--template-url https://s3.amazonaws.com/my-bucket/template.yaml \
--capabilities CAPABILITY_IAM
# Pass parameters
aws cloudformation create-stack \
--stack-name my-stack \
--template-body file://template.yaml \
--parameters ParameterKey=Env,ParameterValue=prod \
ParameterKey=InstanceType,ParameterValue=t3.medium
# Add tags
aws cloudformation create-stack \
--stack-name my-stack \
--template-body file://template.yaml \
--tags Key=Team,Value=platform Key=CostCenter,Value=engbashยทUpdate & delete
# Update a stack aws cloudformation update-stack \ --stack-name my-stack \ --template-body file://template.yaml \ --capabilities CAPABILITY_IAM # Update with previous template (params change only) aws cloudformation update-stack \ --stack-name my-stack \ --use-previous-template \ --parameters ParameterKey=Env,ParameterValue=staging # Delete a stack aws cloudformation delete-stack --stack-name my-stack # Retain specific resources on delete aws cloudformation delete-stack \ --stack-name my-stack \ --retain-resources MyS3Bucket MyLogGroup
bashยทWait & describe
# Wait until stack creation completes aws cloudformation wait stack-create-complete --stack-name my-stack # Wait until update completes aws cloudformation wait stack-update-complete --stack-name my-stack # Wait until delete completes aws cloudformation wait stack-delete-complete --stack-name my-stack # Describe stack (status, outputs, params) aws cloudformation describe-stacks --stack-name my-stack # Get just stack status aws cloudformation describe-stacks \ --stack-name my-stack \ --query "Stacks[0].StackStatus" --output text # List all stacks aws cloudformation list-stacks \ --stack-status-filter CREATE_COMPLETE UPDATE_COMPLETE
Change Sets
Preview changes before applying them to a stack
bashยทCreate & describe a change set
# Create a change set for an existing stack aws cloudformation create-change-set \ --stack-name my-stack \ --change-set-name my-changes \ --template-body file://template.yaml \ --capabilities CAPABILITY_IAM # Create change set for a new stack aws cloudformation create-change-set \ --stack-name my-stack \ --change-set-name initial \ --change-set-type CREATE \ --template-body file://template.yaml \ --capabilities CAPABILITY_IAM # Wait for change set to be ready aws cloudformation wait change-set-create-complete \ --stack-name my-stack \ --change-set-name my-changes # Describe what would change aws cloudformation describe-change-set \ --stack-name my-stack \ --change-set-name my-changes
bashยทExecute & delete a change set
# Execute (apply) the change set aws cloudformation execute-change-set \ --stack-name my-stack \ --change-set-name my-changes # List all change sets for a stack aws cloudformation list-change-sets --stack-name my-stack # Delete a change set without applying aws cloudformation delete-change-set \ --stack-name my-stack \ --change-set-name my-changes
Stack Events & Resources
Inspect events, resources, and outputs
bashยทEvents
# List stack events (most recent first)
aws cloudformation describe-stack-events --stack-name my-stack
# Tail events during a deployment (bash loop)
while true; do
aws cloudformation describe-stack-events \
--stack-name my-stack \
--query "StackEvents[?ResourceStatus!='UPDATE_COMPLETE'][].{Time:Timestamp,Status:ResourceStatus,Resource:LogicalResourceId,Reason:ResourceStatusReason}" \
--output table
sleep 5
done
# Get only FAILED events
aws cloudformation describe-stack-events \
--stack-name my-stack \
--query "StackEvents[?contains(ResourceStatus,'FAILED')].{Resource:LogicalResourceId,Reason:ResourceStatusReason}" \
--output tablebashยทResources & outputs
# List all physical resources in a stack aws cloudformation list-stack-resources --stack-name my-stack # Get a specific resource's physical ID aws cloudformation list-stack-resources \ --stack-name my-stack \ --query "StackResourceSummaries[?LogicalResourceId=='MyBucket'].PhysicalResourceId" \ --output text # Get stack outputs aws cloudformation describe-stacks \ --stack-name my-stack \ --query "Stacks[0].Outputs" --output table # Get a specific output value aws cloudformation describe-stacks \ --stack-name my-stack \ --query "Stacks[0].Outputs[?OutputKey=='BucketName'].OutputValue" \ --output text
Drift Detection
Detect and inspect configuration drift from the template
bashยทDetect & describe drift
# Start drift detection
aws cloudformation detect-stack-drift --stack-name my-stack
# Get the drift detection operation ID
DRIFT_ID=$(aws cloudformation detect-stack-drift \
--stack-name my-stack \
--query "StackDriftDetectionId" --output text)
# Check detection status
aws cloudformation describe-stack-drift-detection-status \
--stack-drift-detection-id $DRIFT_ID
# List drifted resources
aws cloudformation describe-stack-resource-drifts \
--stack-name my-stack \
--stack-resource-drift-status-filters MODIFIED DELETED
# Detect drift on all stacks (bash loop)
aws cloudformation list-stacks \
--stack-status-filter CREATE_COMPLETE UPDATE_COMPLETE \
--query "StackSummaries[].StackName" --output text \
| tr '\t' '\n' | while read STACK; do
echo "Checking $STACK..."
aws cloudformation detect-stack-drift --stack-name "$STACK"
doneStack Policies
Protect resources from being replaced or deleted during updates
bashยทSet & override stack policy
# Set a stack policy (deny all updates by default)
aws cloudformation set-stack-policy \
--stack-name my-stack \
--stack-policy-body '{
"Statement": [
{
"Effect": "Deny",
"Action": "Update:*",
"Principal": "*",
"Resource": "LogicalResourceId/ProductionDatabase"
},
{
"Effect": "Allow",
"Action": "Update:*",
"Principal": "*",
"Resource": "*"
}
]
}'
# Get current stack policy
aws cloudformation get-stack-policy --stack-name my-stack
# Temporarily override policy during an update
aws cloudformation update-stack \
--stack-name my-stack \
--template-body file://template.yaml \
--stack-policy-during-update-body '{"Statement":[{"Effect":"Allow","Action":"Update:*","Principal":"*","Resource":"*"}]}'StackSets
Deploy stacks across multiple accounts and regions
bashยทCreate & manage StackSets
# Create a StackSet aws cloudformation create-stack-set \ --stack-set-name my-stackset \ --template-body file://template.yaml \ --capabilities CAPABILITY_IAM \ --permission-model SERVICE_MANAGED \ --auto-deployment Enabled=true,RetainStacksOnAccountRemoval=false # Add stack instances to accounts/regions aws cloudformation create-stack-instances \ --stack-set-name my-stackset \ --accounts 111122223333 444455556666 \ --regions us-east-1 eu-west-1 # Update all instances aws cloudformation update-stack-set \ --stack-set-name my-stackset \ --template-body file://template.yaml \ --operation-preferences MaxConcurrentPercentage=25,FailureTolerancePercentage=10 # List instances aws cloudformation list-stack-instances --stack-set-name my-stackset # Delete instances aws cloudformation delete-stack-instances \ --stack-set-name my-stackset \ --accounts 111122223333 \ --regions us-east-1 \ --no-retain-stacks # Delete the StackSet (must have no instances) aws cloudformation delete-stack-set --stack-set-name my-stackset
Template Validation & Packaging
Validate, package, and deploy templates with nested stacks
bashยทValidate & lint
# Validate template syntax (does not check IAM/logic) aws cloudformation validate-template --template-body file://template.yaml # Validate from S3 aws cloudformation validate-template \ --template-url https://s3.amazonaws.com/my-bucket/template.yaml # Estimate monthly cost of the template aws cloudformation estimate-template-cost \ --template-body file://template.yaml \ --parameters ParameterKey=InstanceType,ParameterValue=m5.large # cfn-lint โ third-party deep linting (pip install cfn-lint) cfn-lint template.yaml cfn-lint template.yaml --include-checks W cfn-lint template.yaml --ignore-checks W3002
bashยทPackage & deploy (SAM/nested stacks)
# Package: upload local artifacts to S3 and rewrite template aws cloudformation package \ --template-file template.yaml \ --s3-bucket my-artifacts-bucket \ --s3-prefix cfn \ --output-template-file packaged.yaml # Deploy packaged template aws cloudformation deploy \ --template-file packaged.yaml \ --stack-name my-stack \ --capabilities CAPABILITY_IAM CAPABILITY_AUTO_EXPAND # deploy with parameters and tags aws cloudformation deploy \ --template-file packaged.yaml \ --stack-name my-stack \ --capabilities CAPABILITY_IAM \ --parameter-overrides Env=prod InstanceType=t3.medium \ --tags Team=platform \ --no-fail-on-empty-changeset
Template Anatomy
Structure, parameters, mappings, conditions, outputs, and transforms
yamlยทTemplate skeleton
AWSTemplateFormatVersion: "2010-09-09"
Description: "My application stack"
# Optional: SAM or CloudFormation macro transforms
Transform: AWS::Serverless-2016-10-31
Parameters:
Env:
Type: String
AllowedValues: [dev, staging, prod]
Default: dev
InstanceType:
Type: String
Default: t3.micro
Mappings:
AmiByRegion:
us-east-1:
ami: ami-0abcdef1234567890
eu-west-1:
ami: ami-0fedcba9876543210
Conditions:
IsProd: !Equals [!Ref Env, prod]
IsNotProd: !Not [!Condition IsProd]
Resources:
MyBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub "my-app-${Env}-${AWS::AccountId}"
VersioningConfiguration:
Status: !If [IsProd, Enabled, Suspended]
Outputs:
BucketName:
Value: !Ref MyBucket
Export:
Name: !Sub "${AWS::StackName}-BucketName"yamlยทIntrinsic functions
# Ref โ logical resource ID or parameter value
BucketName: !Ref MyBucket
# Sub โ string interpolation
Name: !Sub "app-${Env}-${AWS::Region}"
ARN: !Sub "arn:aws:s3:::${MyBucket}/*"
# GetAtt โ attribute of a resource
RoleArn: !GetAtt MyRole.Arn
DNS: !GetAtt MyALB.DNSName
# Join
Policy: !Join
- ""
- - "arn:aws:s3:::"
- !Ref MyBucket
- "/*"
# Select โ pick item from list
AZ: !Select [0, !GetAZs ""]
# Split
Parts: !Split [",", !Ref CsvParam]
# If โ conditional value
Size: !If [IsProd, 100, 20]
# ImportValue โ cross-stack reference
VpcId: !ImportValue
!Sub "${NetworkStack}-VpcId"
# FindInMap
Ami: !FindInMap [AmiByRegion, !Ref "AWS::Region", ami]
# Cidr โ generate CIDR blocks
Subnets: !Cidr [!Ref VpcCidr, 6, 8]yamlยทResource metadata & helpers
Resources:
MyInstance:
Type: AWS::EC2::Instance
# Creation policy: wait for cfn-signal before marking CREATE_COMPLETE
CreationPolicy:
ResourceSignal:
Count: 1
Timeout: PT10M
# Update policy: rolling update for ASGs
UpdatePolicy:
AutoScalingRollingUpdate:
MinInstancesInService: 1
MaxBatchSize: 1
PauseTime: PT5M
WaitOnResourceSignals: true
# Deletion policy: retain or snapshot on stack delete
DeletionPolicy: Retain # or Snapshot, Delete
# Update replace policy: what to do with old resource on replacement
UpdateReplacePolicy: Retain
Properties:
ImageId: !FindInMap [AmiByRegion, !Ref "AWS::Region", ami]
InstanceType: !Ref InstanceType
UserData:
Fn::Base64: !Sub |
#!/bin/bash
yum update -y
/opt/aws/bin/cfn-signal -e $? \
--stack ${AWS::StackName} \
--resource MyInstance \
--region ${AWS::Region}Pseudo Parameters & SSM
Built-in pseudo parameters and SSM Parameter Store integration
yamlยทPseudo parameters
# Available anywhere in a template via !Ref or !Sub # Current AWS account ID AccountId: !Ref AWS::AccountId # "123456789012" # Region the stack is being deployed to Region: !Ref AWS::Region # "us-east-1" # Stack name Stack: !Ref AWS::StackName # "my-stack" # Full stack ARN StackId: !Ref AWS::StackId # "AWS::NoValue" โ conditionally omit a property Properties: KmsKeyId: !If [IsProd, !Ref MyKey, !Ref AWS::NoValue] # Partition (aws, aws-cn, aws-us-gov) Partition: !Ref AWS::Partition # URL suffix (amazonaws.com or amazonaws.com.cn) Suffix: !Ref AWS::URLSuffix
yamlยทSSM Parameter Store in templates
Parameters:
# Resolve SSM value at deploy time (plaintext)
AmiId:
Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2
# String from SSM
DbPassword:
Type: AWS::SSM::Parameter::Value<String>
Default: /myapp/prod/db-password
# Secure string (encrypted) โ use dynamic reference instead
# SSM secure string can NOT be used as a Parameter type directly
# Dynamic references โ resolved at deployment, not stored in template
Resources:
MySecret:
Type: AWS::RDS::DBInstance
Properties:
# SSM plaintext
DBName: "{{resolve:ssm:/myapp/db-name}}"
# SSM SecureString (KMS-encrypted)
MasterUserPassword: "{{resolve:ssm-secure:/myapp/db-password:1}}"
# Secrets Manager
MasterUserPassword: "{{resolve:secretsmanager:MySecret:SecretString:password}}"Useful Patterns
Nested stacks, custom resources, and stack rollback
yamlยทNested stacks
Resources:
NetworkStack:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: https://s3.amazonaws.com/my-bucket/network.yaml
Parameters:
VpcCidr: "10.0.0.0/16"
Env: !Ref Env
TimeoutInMinutes: 20
AppStack:
Type: AWS::CloudFormation::Stack
DependsOn: NetworkStack
Properties:
TemplateURL: https://s3.amazonaws.com/my-bucket/app.yaml
Parameters:
VpcId: !GetAtt NetworkStack.Outputs.VpcId
SubnetIds: !GetAtt NetworkStack.Outputs.SubnetIdsbashยทRollback & troubleshooting
# Continue a failed rollback (when rollback itself failed)
aws cloudformation continue-update-rollback --stack-name my-stack
# Skip resources that are blocking rollback
aws cloudformation continue-update-rollback \
--stack-name my-stack \
--resources-to-skip MyProblematicResource
# Cancel an in-progress update
aws cloudformation cancel-update-stack --stack-name my-stack
# Describe why a resource failed
aws cloudformation describe-stack-events \
--stack-name my-stack \
--query "StackEvents[?ResourceStatus=='CREATE_FAILED' || ResourceStatus=='UPDATE_FAILED'].{Resource:LogicalResourceId,Reason:ResourceStatusReason}" \
--output table
# Signal a resource manually (e.g. from EC2 UserData)
aws cloudformation signal-resource \
--stack-name my-stack \
--logical-resource-id MyInstance \
--unique-id i-0abc123 \
--status SUCCESS