Skip to content

Cloudformation

This chapter covers AWS CloudFormation as an alternative Infrastructure as Code solution.

CloudFormation is AWS’s native Infrastructure as Code solution.

┌─────────────────────────────────────────────────────────────────────────────┐
│ CloudFormation Overview │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ CloudFormation Process │ │
│ │ │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ Template │───▶│ Create │───▶│ Resources │ │ │
│ │ │ (YAML/JSON)│ │ Stack │ │ Created │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ Features: │
│ ✓ AWS-native - Integrated with all AWS services │
│ ✓ Declarative - Define desired state │
│ ✓ Idempotent - Safe to run multiple times │
│ ✓ Drift detection - Identify manual changes │
│ ✓ Change sets - Preview changes before applying │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ CloudFormation vs Terraform │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Feature CloudFormation Terraform │
│ ────────────────────────────────────────────────────────────────────── │
│ Provider AWS-only Multi-cloud │
│ Language YAML or JSON HCL │
│ State AWS-managed User-managed │
│ Learning Curve Low Moderate │
│ Drift Detection Built-in Manual │
│ Modules Nested Stacks Registry Modules │
│ Updates Change Sets Plan │
│ │
│ When to use CloudFormation: │
│ ✓ AWS-only environments │
│ ✓ Team already familiar with AWS │
│ ✓ Need native drift detection │
│ ✓ Strong AWS service support needed │
│ │
│ When to use Terraform: │
│ ✓ Multi-cloud infrastructure │
│ ✓ Complex modules needed │
│ ✓ Need better state management │
│ ✓ Existing Terraform expertise │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: 'My CloudFormation Template'
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: 'Network Configuration'
Parameters:
- VPCCidr
- SubnetCidr
- Label:
default: 'Compute Configuration'
Parameters:
- InstanceType
Parameters:
VPCCidr:
Type: String
Default: '10.0.0.0/16'
Description: 'CIDR block for VPC'
InstanceType:
Type: String
Default: 't3.micro'
AllowedValues:
- t3.micro
- t3.small
- t3.medium
Description: 'EC2 instance type'
Mappings:
RegionAMI:
us-east-1:
AMI: 'ami-0c55b159cbfafe1f0'
us-west-2:
AMI: 'ami-0892d3c7ee96c0bf7'
Conditions:
IsProd: !Equals [!Ref Environment, 'prod']
Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref VPCCidr
EnableDnsHostnames: true
EnableDnsSupport: true
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-vpc'
Subnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: '10.0.1.0/24'
AvailabilityZone: !Select [0, !GetAZs '']
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-subnet'
Outputs:
VPCId:
Description: 'VPC ID'
Value: !Ref VPC
Export:
Name: !Sub '${AWS::StackName}-VPCID'
SubnetId:
Description: 'Subnet ID'
Value: !Ref Subnet
# Intrinsic Functions Examples
# Ref - Reference parameter or resource
VPCId: !Ref MyVPC
# Fn::GetAtt - Get attribute from resource
InstancePrivateIP: !GetAtt MyInstance.PrivateIp
# Fn::Sub - Substitute values
StackName: !Sub '${AWS::StackName}-resource'
# Fn::Join - Join strings
JoinExample: !Join ['', ['arn:', !Ref AWS::Partition, ':ec2:', !Ref AWS::Region, ':', !Ref AWS::AccountId, ':instance/', !Ref MyInstance]]
# Fn::Select - Select from list
AZ: !Select [0, !GetAZs '']
# Fn::Equals - Compare values
IsProduction: !Equals [!Ref Environment, 'prod']
# Fn::If - Conditional
ConditionalSG: !If [CreateProductionSG, !Ref ProductionSG, !Ref DevSG]
# Fn::Not - Negate
NotDev: !Not [!Equals [!Ref Environment, 'dev']]
# Fn::And - Logical AND
Both: !And [!Condition IsProd, !Condition HasBackup]
# Fn::Or - Logical OR
Either: !Or [!Condition IsProd, !Condition HasBackup]
# Fn::FindInMap - Look up in mapping
AMI: !FindInMap [RegionAMI, !Ref AWS::Region, AMI]
Terminal window
# Create stack
aws cloudformation create-stack \
--stack-name my-stack \
--template-body file://template.yaml \
--parameters ParameterKey=InstanceType,ParameterValue=t3.micro \
--capabilities CAPABILITY_IAM
# Update stack
aws cloudformation update-stack \
--stack-name my-stack \
--template-body file://template.yaml \
--parameters ParameterKey=InstanceType,ParameterValue=t3.small
# Describe stack
aws cloudformation describe-stacks \
--stack-name my-stack
# Delete stack
aws cloudformation delete-stack \
--stack-name my-stack
# List stacks
aws cloudformation list-stacks \
--stack-status-filter CREATE_COMPLETE
# Get template
aws cloudformation get-template \
--stack-name my-stack
# List stack resources
aws cloudformation list-stack-resources \
--stack-name my-stack
Terminal window
# Create change set
aws cloudformation create-change-set \
--stack-name my-stack \
--change-set-name my-changes \
--template-body file://template.yaml \
--parameters ParameterKey=InstanceType,ParameterValue=t3.medium
# Describe change set
aws cloudformation describe-change-set \
--stack-name my-stack \
--change-set-name my-changes
# Execute change set
aws cloudformation execute-change-set \
--stack-name my-stack \
--change-set-name my-changes
# Delete change set
aws cloudformation delete-change-set \
--stack-name my-stack \
--change-set-name my-changes
┌─────────────────────────────────────────────────────────────────────────────┐
│ Drift Detection │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Detect Drift │ │
│ │ │ │
│ │ aws cloudformation detect-stack-drift \ │ │
│ │ --stack-name my-stack │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Check Drift Status │ │
│ │ │ │
│ │ aws cloudformation describe-stack-drift-detection-status \ │ │
│ │ --stack-name my-stack │ │
│ │ │ │
│ │ Returns: │ │
│ │ • DRIFTED - Stack has drifted │ │
│ │ • IN_SYNC - No drift detected │ │
│ │ • UNKNOWN - Detection still in progress │ │
│ │ • NOT_CHECKED - Drift not detected yet │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
# Parent stack
Resources:
VPCStack:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: https://s3.amazonaws.com/my-bucket/vpc-template.yaml
Parameters:
VPCCidr: 10.0.0.0/16
Environment: !Ref Environment
ComputeStack:
Type: AWS::CloudFormation::Stack
TemplateURL: https://s3.amazonaws.com/my-bucket/compute-template.yaml
Parameters:
VPCId: !GetAtt VPCStack.Outputs.VPCId
SubnetIds: !GetAtt VPCStack.Outputs.SubnetIds
┌─────────────────────────────────────────────────────────────────────────────┐
│ StackSets Overview │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ StackSets │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────────────┐ │ │
│ │ │ StackSet Template │ │ │
│ │ └──────────────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ┌──────────────────┼──────────────────┐ │ │
│ │ ▼ ▼ ▼ │ │
│ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ │
│ │ │ Account 1 │ │ Account 2 │ │ Account 3 │ │ │
│ │ │ Region A │ │ Region A │ │ Region B │ │ │
│ │ └────────────┘ └────────────┘ └────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ Use cases: │
│ ✓ Deploy to multiple accounts │
│ ✓ Deploy to multiple regions │
│ ✓ Centralized compliance │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Terminal window
# Create StackSet
aws cloudformation create-stack-set \
--stack-set-name my-stackset \
--template-body file://template.yaml \
--capabilities CAPABILITY_IAM
# Add stack instances
aws cloudformation create-stack-instances \
--stack-set-name my-stackset \
--accounts '["123456789012", "987654321098"]' \
--regions '["us-east-1", "us-west-2"]' \
--operation-indication-bearing
# Update StackSet
aws cloudformation update-stack-set \
--stack-set-name my-stackset \
--template-body file://updated-template.yaml
# Best Practices
# 1. Use Parameters for flexibility
Parameters:
Environment:
Type: String
AllowedValues:
- dev
- staging
- prod
Description: 'Deployment environment'
# 2. Use Mappings for region-specific values
Mappings:
RegionMap:
us-east-1:
HVM64: 'ami-0c55b159cbfafe1f0'
us-west-2:
HVM64: 'ami-0892d3c7ee96c0bf7'
# 3. Use Outputs for cross-stack references
Outputs:
VPCId:
Value: !Ref VPC
Export:
Name: !Sub '${AWS::StackName}-VPCID'
# 4. Enable termination protection
Resources:
MyStack:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: https://s3.amazonaws.com/bucket/template.yaml
TerminationProtection: true
# 5. Use Stack Policies
aws cloudformation set-stack-policy \
--stack-name my-stack \
--stack-policy-body file://policy.json
{
"Statement" : [
{
"Effect" : "Allow",
"Action" : "Update:*",
"Principal": "*",
"Resource" : "*"
},
{
"Effect" : "Deny",
"Action" : "Update:*",
"Principal": "*",
"Resource" : "LogicalResourceId/ProductionDatabase"
}
]
}

In this chapter, you learned:

  • CloudFormation Overview: AWS-native IaC solution
  • CloudFormation vs Terraform: When to choose which
  • Template Structure: YAML/JSON format, sections
  • Intrinsic Functions: Ref, GetAtt, Sub, Select, If, etc.
  • Stack Operations: Create, update, delete, describe
  • Change Sets: Preview changes safely
  • Drift Detection: Identify manual changes
  • Nested Stacks: Modular templates
  • StackSets: Multi-account, multi-region deployments
  • Best Practices: Parameters, mappings, outputs, policies