AWS CloudFormation lets you manage your AWS infrastructure using code. Instead of setting up resources by hand, you write a template in JSON or YAML and let CloudFormation handle the rest. This guide covers the core concepts, stack types, lifecycle states, and real troubleshooting patterns from hands-on experience.
Why CloudFormation?
- Automation — define once, deploy anywhere, no manual clicking
- Consistency — same template = same environment every time
- Version control — templates are code, store them in Git
- Cost control — delete a whole stack with one command, no orphaned resources
- Scalability — update templates to grow infrastructure as the project grows
Types of Stacks
1. Single Stack
All resources in one template. Good for small projects.
AWSTemplateFormatVersion: '2010-09-09'
Resources:
BlogSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group for blog app
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
BlogInstanceRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: ec2.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
BlogEC2Instance:
Type: AWS::EC2::Instance
Properties:
InstanceType: t2.micro
ImageId: ami-0c55b159cbfafe1f0
SecurityGroupIds:
- !Ref BlogSecurityGroup
IamInstanceProfile: !Ref BlogInstanceProfile
BlogStaticBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub blog-static-files-${AWS::StackName}
2. Nested Stacks
Split large templates into modular child stacks. A parent stack references child stacks stored in S3. Good for separating networking, compute, and storage concerns.
Parent template:
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
VpcCidr:
Type: String
Default: 10.1.0.0/16
Resources:
NetworkStack:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: https://s3.amazonaws.com/my-bucket/ecommerce-network.yaml
Parameters:
VpcCidr: !Ref VpcCidr
AppServerStack:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: https://s3.amazonaws.com/my-bucket/ecommerce-app.yaml
Parameters:
SubnetId: !GetAtt NetworkStack.Outputs.PublicSubnetId
SecurityGroupId: !GetAtt NetworkStack.Outputs.AppSecurityGroupId
DatabaseStack:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: https://s3.amazonaws.com/my-bucket/ecommerce-database.yaml
Parameters:
SubnetId: !GetAtt NetworkStack.Outputs.PrivateSubnetId
SecurityGroupId: !GetAtt NetworkStack.Outputs.DBSecurityGroupId
Child network template (ecommerce-network.yaml):
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
VpcCidr:
Type: String
Resources:
EcommerceVPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref VpcCidr
PublicSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref EcommerceVPC
CidrBlock: 10.1.1.0/24
AppSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group for app servers
VpcId: !Ref EcommerceVPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
DBSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group for database
VpcId: !Ref EcommerceVPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 3306
ToPort: 3306
SourceSecurityGroupId: !Ref AppSecurityGroup
Outputs:
PublicSubnetId:
Value: !Ref PublicSubnet
PrivateSubnetId:
Value: !Ref PrivateSubnet
AppSecurityGroupId:
Value: !Ref AppSecurityGroup
DBSecurityGroupId:
Value: !Ref DBSecurityGroup
Lesson learned: A parent stack with 8 child stacks took 25 minutes to deploy vs 12 minutes for a single stack. Keep nesting manageable.
3. Stacks with Exports
Share values between independent stacks using Outputs + Export.
Network stack exports a subnet ID:
Outputs:
SubnetIdExport:
Value: !Ref AppSubnet
Export:
Name: AppSubnetExport
Storage stack imports it:
Resources:
DBSubnetGroup:
Type: AWS::RDS::DBSubnetGroup
Properties:
DBSubnetGroupDescription: Subnet group for RDS
SubnetIds:
- !ImportValue AppSubnetExport
Stack Lifecycle States
Understanding states helps you debug what's happening and why.
| State | Meaning |
|---|---|
CREATE_IN_PROGRESS |
Resources are being provisioned |
CREATE_COMPLETE |
Stack created successfully |
CREATE_FAILED |
Something went wrong during creation |
UPDATE_IN_PROGRESS |
A change is being applied |
UPDATE_COMPLETE |
Update succeeded |
UPDATE_ROLLBACK_IN_PROGRESS |
Update failed, rolling back |
UPDATE_ROLLBACK_COMPLETE |
Rollback finished, back to previous state |
DELETE_IN_PROGRESS |
Stack deletion in progress |
DELETE_COMPLETE |
All resources removed |
REVIEW_IN_PROGRESS |
Change set being validated |
ROLLBACK_IN_PROGRESS |
Initial creation failed, cleaning up |
ROLLBACK_COMPLETE |
Cleanup done after failed creation |
Troubleshooting Common Issues
1. Syntax errors
Validate before deploying:
aws cloudformation validate-template \
--template-body file://template.yaml \
--region us-west-2
2. Wrong resource settings
Check the AWS CloudFormation docs for valid property names. A single typo lands you in CREATE_FAILED.
3. Stuck in REVIEW_IN_PROGRESS
Cancel the update or delete the change set:
aws cloudformation cancel-update-stack \
--stack-name AppBase-US-West-2 \
--region us-west-2
aws cloudformation delete-change-set \
--stack-name AppBase-US-West-2 \
--change-set-name FailedChange \
--region us-west-2
Nuclear option if needed:
aws cloudformation delete-stack \
--stack-name AppBase-US-West-2 \
--region us-west-2
4. Pipeline failures
If a CI/CD pipeline (GitHub Actions, Azure DevOps) fails mid-deploy, add a cleanup step that deletes failed change sets before retrying. Otherwise the next run will fail immediately on the stale change set.
5. Dependency issues
Use DependsOn when a resource needs another to exist first:
Resources:
MyLambda:
Type: AWS::Lambda::Function
DependsOn: MyDynamoDBStream
Properties:
...
Tips for Getting Started
- Start simple — one EC2 instance, get it deploying cleanly before adding complexity
- Use parameters — make templates reusable across environments from day one
- Test on a disposable stack — never test changes directly on production
- Watch the Events tab — it's the most useful debugging surface in the console
- Plan for rollback — always include cleanup steps in your pipeline
Conclusion
CloudFormation is foundational infrastructure-as-code on AWS. Single stacks for simplicity, nested stacks for modularity, exports for cross-stack sharing. Know the lifecycle states, validate templates before deploying, and build rollback handling into your pipelines from the start.
Originally published on Medium.
Top comments (0)