CloudFormation Basics
Template Structure
AWSTemplateFormatVersion: '2010-09-09'
Description: 'My application stack'
Parameters:
Environment:
Type: String
AllowedValues: [dev, staging, prod]
Default: dev
Mappings:
RegionAMI:
us-east-1:
ami: ami-0abcdef1234567890
us-west-2:
ami: ami-0987654321fedcba0
Conditions:
IsProd: !Equals [!Ref Environment, prod]
Resources:
MyBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub 'my-app-${Environment}-${AWS::AccountId}'
Outputs:
BucketArn:
Value: !GetAtt MyBucket.Arn
Export:
Name: !Sub '${AWS::StackName}-BucketArn'
Parameters & Pseudo Parameters
Parameters:
InstanceType:
Type: String
Default: t3.micro
AllowedValues: [t3.micro, t3.small, t3.medium]
Description: EC2 instance type
DBPassword:
Type: String
NoEcho: true # Mask in console
MinLength: 8
SubnetIds:
Type: List<AWS::EC2::Subnet::Id>
# Pseudo parameters (always available)
# AWS::AccountId - 123456789012
# AWS::Region - us-east-1
# AWS::StackName - my-stack
# AWS::StackId - arn:aws:cloudformation:...
# AWS::NoValue - removes property when used
# Intrinsic functions
!Ref ParameterName # reference parameter/resource
!Sub 'Hello ${AWS::Region}' # string substitution
!GetAtt Resource.Attribute # get resource attribute
!Select [0, !Ref SubnetIds] # select from list
!If [IsProd, t3.large, t3.micro] # conditional
Resource Examples
Resources:
WebServerSG:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Web server SG
VpcId: !Ref VpcId
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
WebServer:
Type: AWS::EC2::Instance
DependsOn: WebServerSG
Properties:
InstanceType: !Ref InstanceType
ImageId: !FindInMap [RegionAMI, !Ref AWS::Region, ami]
SecurityGroupIds: [!Ref WebServerSG]
UserData:
Fn::Base64: !Sub |
#!/bin/bash
yum install -y httpd
systemctl start httpd
WebEIP:
Type: AWS::EC2::EIP
Properties:
InstanceId: !Ref WebServer
Cross-Stack References
# Stack A exports
Outputs:
VpcId:
Value: !Ref MyVPC
Export:
Name: NetworkStack-VpcId
SubnetIds:
Value: !Join [',', [!Ref SubnetA, !Ref SubnetB]]
Export:
Name: NetworkStack-SubnetIds
# Stack B imports
Resources:
AppServer:
Type: AWS::EC2::Instance
Properties:
SubnetId: !Select
- 0
- !Split [',', !ImportValue NetworkStack-SubnetIds]
# CLI: list exports
aws cloudformation list-exports
Stack Operations & Change Sets
# Create stack
aws cloudformation create-stack \
--stack-name my-stack \
--template-body file://template.yaml \
--parameters ParameterKey=Environment,ParameterValue=prod \
--capabilities CAPABILITY_IAM
# Update with change set (review before applying)
aws cloudformation create-change-set \
--stack-name my-stack \
--change-set-name my-changes \
--template-body file://template.yaml
aws cloudformation describe-change-set \
--stack-name my-stack \
--change-set-name my-changes
aws cloudformation execute-change-set \
--stack-name my-stack \
--change-set-name my-changes
# Rollback on failure
aws cloudformation continue-update-rollback --stack-name my-stack
# Delete stack
aws cloudformation delete-stack --stack-name my-stack
Stack Policies & Drift Detection
# Protect resources from update/delete
aws cloudformation set-stack-policy \
--stack-name my-stack \
--stack-policy-body '{
"Statement": [{
"Effect": "Deny",
"Action": "Update:Replace",
"Principal": "*",
"Resource": "LogicalResourceId/ProductionDB"
}]
}'
# Detect configuration drift
aws cloudformation detect-stack-drift --stack-name my-stack
aws cloudformation describe-stack-drift-detection-status \
--stack-drift-detection-id abc123