AWS CloudFormation: Infrastructure as Code Complete Guide (2026)
CloudFormation is AWS's native Infrastructure as Code service. Define your entire AWS infrastructure in YAML or JSON templates, and CloudFormation handles creation, updates and deletion in the correct dependency order. Unlike CDK or Terraform, CloudFormation templates are natively understood by AWS and have the broadest service coverage — every new AWS service gets CloudFormation support on day one.
Table of Contents
1. Template Anatomy
AWSTemplateFormatVersion: '2010-09-09'
Description: 'MyApp production infrastructure'
Parameters: # Input values at deploy time
...
Mappings: # Static lookup tables (AMI IDs per region)
...
Conditions: # Boolean conditions (enable/disable resources)
...
Resources: # REQUIRED — AWS resources to create
...
Outputs: # Export values for cross-stack use
...
2. Parameters and Mappings
Parameters:
Environment:
Type: String
Default: staging
AllowedValues: [staging, production]
Description: Deployment environment
InstanceType:
Type: String
Default: t3.medium
AllowedValues: [t3.small, t3.medium, t3.large, m6i.large]
DBPassword:
Type: String
NoEcho: true # Masked in console and CLI output
MinLength: 8
Description: RDS master password
Mappings:
RegionAMI:
us-east-1:
AmazonLinux2023: ami-0abcdef1234567890
us-west-2:
AmazonLinux2023: ami-0fedcba9876543210
Conditions:
IsProduction: !Equals [!Ref Environment, production]
IsNotProduction: !Not [!Condition IsProduction]
3. Resources
Resources:
AppSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: App server security group
VpcId: !Ref VpcId
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 8080
ToPort: 8080
SourceSecurityGroupId: !Ref ALBSecurityGroup
AppLaunchTemplate:
Type: AWS::EC2::LaunchTemplate
Properties:
LaunchTemplateData:
ImageId: !FindInMap [RegionAMI, !Ref AWS::Region, AmazonLinux2023]
InstanceType: !Ref InstanceType
SecurityGroupIds: [!Ref AppSecurityGroup]
IamInstanceProfile:
Arn: !GetAtt AppInstanceProfile.Arn
UserData:
Fn::Base64: !Sub |
#!/bin/bash
yum update -y
aws s3 cp s3://${ArtifactBucket}/app.jar /opt/app/
systemctl start myapp
AppAutoScalingGroup:
Type: AWS::AutoScaling::AutoScalingGroup
Properties:
LaunchTemplate:
LaunchTemplateId: !Ref AppLaunchTemplate
Version: !GetAtt AppLaunchTemplate.LatestVersionNumber
MinSize: !If [IsProduction, '2', '1']
MaxSize: !If [IsProduction, '10', '3']
TargetGroupARNs: [!Ref AppTargetGroup]
VPCZoneIdentifier: !Ref PrivateSubnets
UpdatePolicy:
AutoScalingRollingUpdate:
MinInstancesInService: 1
MaxBatchSize: 2
4. Intrinsic Functions
| Function | Short Form | Purpose |
|---|---|---|
| Ref | !Ref | Reference parameter or resource ID |
| Fn::GetAtt | !GetAtt | Get resource attribute (e.g., ARN) |
| Fn::Sub | !Sub | String substitution with variable replacement |
| Fn::Join | !Join | Join list with delimiter |
| Fn::Select | !Select | Select item from list by index |
| Fn::FindInMap | !FindInMap | Look up value in Mappings |
| Fn::If | !If | Conditional value based on Condition |
| Fn::ImportValue | !ImportValue | Import exported output from another stack |
# Examples
BucketArn: !GetAtt MyBucket.Arn
LambdaInvokeArn: !Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/functions/${MyFunction.Arn}/invocations"
DBEndpoint: !Join [':', [!GetAtt Database.Endpoint.Address, !GetAtt Database.Endpoint.Port]]
5. Outputs and Cross-Stack References
# Stack A — exports values
Outputs:
VpcId:
Description: VPC ID for application stacks
Value: !Ref AppVPC
Export:
Name: !Sub "${AWS::StackName}-VpcId"
PrivateSubnets:
Value: !Join [',', [!Ref PrivateSubnet1, !Ref PrivateSubnet2]]
Export:
Name: !Sub "${AWS::StackName}-PrivateSubnets"
# Stack B — imports values from Stack A
Resources:
AppInstance:
Type: AWS::EC2::Instance
Properties:
SubnetId: !Select [0, !Split [',', !ImportValue 'network-stack-PrivateSubnets']]
6. Nested Stacks
# Parent stack — composes nested stacks
Resources:
NetworkStack:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: !Sub "https://s3.amazonaws.com/${TemplateBucket}/network.yaml"
Parameters:
Environment: !Ref Environment
AppStack:
Type: AWS::CloudFormation::Stack
DependsOn: NetworkStack
Properties:
TemplateURL: !Sub "https://s3.amazonaws.com/${TemplateBucket}/app.yaml"
Parameters:
VpcId: !GetAtt NetworkStack.Outputs.VpcId
PrivateSubnets: !GetAtt NetworkStack.Outputs.PrivateSubnets
7. Change Sets
# Create a change set to preview changes before applying
aws cloudformation create-change-set \
--stack-name myapp-production \
--change-set-name update-instance-type \
--template-body file://template.yaml \
--parameters ParameterKey=InstanceType,ParameterValue=m6i.large
# Review the change set
aws cloudformation describe-change-set \
--stack-name myapp-production \
--change-set-name update-instance-type
# Execute (apply changes)
aws cloudformation execute-change-set \
--stack-name myapp-production \
--change-set-name update-instance-type
8. StackSets (Multi-Account, Multi-Region)
# Deploy the same stack to multiple accounts and regions
aws cloudformation create-stack-set \
--stack-set-name baseline-security \
--template-url https://s3.amazonaws.com/mybucket/security-baseline.yaml \
--permission-model SERVICE_MANAGED \
--auto-deployment Enabled=true,RetainStacksOnAccountRemoval=false
# Deploy to all accounts in an AWS Organizations OU
aws cloudformation create-stack-instances \
--stack-set-name baseline-security \
--deployment-targets OrganizationalUnitIds=ou-root-abc123 \
--regions us-east-1 us-west-2 eu-west-1 \
--operation-preferences FailureTolerancePercentage=20,MaxConcurrentPercentage=25
Frequently Asked Questions
CloudFormation vs CDK vs Terraform — which should I use?
CloudFormation: native AWS, best service coverage, verbose YAML/JSON. CDK: write in Python/TypeScript/Java, generates CloudFormation, excellent for complex logic. Terraform: multi-cloud, HCL syntax, huge community, external state. For AWS-only teams: CDK is the modern choice. For multi-cloud: Terraform.
How do I prevent CloudFormation from deleting critical resources?
Set DeletionPolicy: Retain on resources like RDS, S3 buckets and EFS that contain data. Also enable Termination Protection on the stack itself: aws cloudformation update-termination-protection --stack-name mystack --enable-termination-protection.
What is stack drift and how do I detect it?
Drift occurs when someone manually modifies a CloudFormation-managed resource outside of CloudFormation. Run drift detection: aws cloudformation detect-stack-drift --stack-name mystack. Review drifted resources and either update CloudFormation to match reality or remediate the manual change.
How do I handle CloudFormation rollbacks?
When a stack update fails, CloudFormation automatically rolls back. If the rollback itself fails (ROLLBACK_FAILED state), you must manually fix the issue or skip the failing resources: aws cloudformation continue-update-rollback --stack-name mystack --resources-to-skip LogicalResourceId1.