CloudFormation: Stacks, Changesets & Best Practices
CLI: Stack Operations
# Deploy stack
aws cloudformation deploy --template-file template.yaml --stack-name my-app-dev --parameter-overrides Environment=dev DbPassword=secret123 --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM --region us-east-1
# Create stack (alternative — more control)
aws cloudformation create-stack --stack-name my-app --template-body file://template.yaml --parameters ParameterKey=Environment,ParameterValue=dev --capabilities CAPABILITY_IAM
# Update stack
aws cloudformation update-stack --stack-name my-app --template-body file://template.yaml --parameters ParameterKey=Environment,ParameterValue=prod
# Wait for completion
aws cloudformation wait stack-create-complete --stack-name my-app
aws cloudformation wait stack-update-complete --stack-name my-app
# Describe stack
aws cloudformation describe-stacks --stack-name my-app
aws cloudformation describe-stack-events --stack-name my-app
# Delete stack
aws cloudformation delete-stack --stack-name my-app
aws cloudformation wait stack-delete-complete --stack-name my-app
# Validate template
aws cloudformation validate-template --template-body file://template.yamlChangesets
Changesets let you preview what CloudFormation will do before executing. Always use changesets for production updates.
# Create changeset (preview changes)
aws cloudformation create-change-set --stack-name my-app --template-body file://template.yaml --change-set-name my-update-$(date +%Y%m%d) --parameters ParameterKey=InstanceType,ParameterValue=t3.small
# View changeset (what will be added/modified/removed)
aws cloudformation describe-change-set --stack-name my-app --change-set-name my-update-20240315
# Execute changeset
aws cloudformation execute-change-set --stack-name my-app --change-set-name my-update-20240315
# Delete changeset (if you decide not to apply)
aws cloudformation delete-change-set --stack-name my-app --change-set-name my-update-20240315Intrinsic Functions
# !Ref — reference a parameter or resource
VpcId: !Ref MyVPC
# !Sub — string substitution
Name: !Sub '${AWS::StackName}-web-${Environment}'
# AWS pseudo-parameters: AWS::AccountId, AWS::Region, AWS::StackName, AWS::NoValue
# !GetAtt — get attribute of a resource
RoleArn: !GetAtt MyRole.Arn
BucketDomainName: !GetAtt MyBucket.DomainName
# !FindInMap — lookup in Mappings
ImageId: !FindInMap [RegionAMI, !Ref AWS::Region, AMI]
# !Select — pick item from list
AZ: !Select [0, !GetAZs ''] # first AZ in region
# !Split — split string to list
Parts: !Split [',', 'a,b,c']
# !Join — join list to string
BucketName: !Join ['-', [!Ref AWS::StackName, !Ref Environment, 'data']]
# !If — conditional value
MultiAZ: !If [IsProduction, true, false]
OptionalResource:
Type: AWS::S3::Bucket
Condition: IsProduction # only create if condition is true
# !ImportValue — cross-stack reference
VpcId: !ImportValue 'network-stack-VpcId'
# !Base64 — encode for UserData
UserData: !Base64 |
#!/bin/bash
echo "Hello" > /tmp/test.txtBest Practices
Use changeset review for all production updates — never update-stack directly in prod.
Set DeletionPolicy: Retain or Snapshot on stateful resources (RDS, S3) to prevent accidental loss.
Use nested stacks to split large templates: AWS::CloudFormation::Stack with TemplateURL.
Use StackSets to deploy the same template across multiple accounts and regions simultaneously.
Store sensitive parameters in AWS Secrets Manager or SSM Parameter Store (SecureString), not template Parameters.
Tag all resources consistently: add a Tags section to every resource for cost allocation and filtering.
Use cfn-lint (CloudFormation Linter) in CI to catch errors before deployment.
Prefer CDK or SAM for complex applications — they generate CloudFormation but with better abstractions.
Keep your own version of these notes — editable, searchable, and organised by your stack.
Start free