AWS for DevOps — CloudFormation Templates
8.1 — CloudFormation Templates
A CloudFormation template is a YAML (or JSON) file that describes your AWS infrastructure. You define what you want — CloudFormation figures out how to create it.
Template Structure
AWSTemplateFormatVersion: "2010-09-09"
Description: "My application infrastructure"
Parameters:
# Input values (like function arguments)
Mappings:
# Static lookup tables
Conditions:
# Conditional logic
Resources:
# AWS resources to create (REQUIRED)
Outputs:
# Values to export / display
The only required section is Resources.
A Complete Example
AWSTemplateFormatVersion: "2010-09-09"
Description: "Web application with EC2 and Security Group"
Parameters:
EnvironmentType:
Type: String
Default: dev
AllowedValues:
- dev
- staging
- production
Description: "Environment type"
InstanceType:
Type: String
Default: t3.micro
AllowedValues:
- t3.micro
- t3.small
- t3.medium
Description: "EC2 instance type"
KeyPairName:
Type: AWS::EC2::KeyPair::KeyName
Description: "SSH key pair"
Mappings:
RegionAMI:
ap-south-1:
HVM64: ami-0abcdef1234567890
us-east-1:
HVM64: ami-0fedcba9876543210
Resources:
WebSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: "Allow HTTP and SSH"
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 10.0.0.0/8
Tags:
- Key: Name
Value: !Sub "web-sg-${EnvironmentType}"
WebServer:
Type: AWS::EC2::Instance
Properties:
InstanceType: !Ref InstanceType
ImageId: !FindInMap [RegionAMI, !Ref "AWS::Region", HVM64]
KeyName: !Ref KeyPairName
SecurityGroupIds:
- !Ref WebSecurityGroup
Tags:
- Key: Name
Value: !Sub "web-server-${EnvironmentType}"
- Key: Environment
Value: !Ref EnvironmentType
UserData:
Fn::Base64: |
#!/bin/bash
yum update -y
yum install -y httpd
systemctl start httpd
systemctl enable httpd
echo "<h1>Hello from CloudFormation</h1>" > /var/www/html/index.html
Outputs:
InstanceId:
Description: "EC2 Instance ID"
Value: !Ref WebServer
PublicIP:
Description: "Public IP address"
Value: !GetAtt WebServer.PublicIp
SecurityGroupId:
Description: "Security Group ID"
Value: !Ref WebSecurityGroup
Export:
Name: !Sub "${EnvironmentType}-web-sg-id"
Template Sections Explained
| Section | Purpose | Required? |
|---|---|---|
AWSTemplateFormatVersion |
Template version (always "2010-09-09") |
No |
Description |
Human-readable description | No |
Parameters |
User inputs at deploy time | No |
Mappings |
Static key-value lookup tables | No |
Conditions |
If/else logic for conditional resources | No |
Resources |
The AWS resources to create | Yes |
Outputs |
Values to display or export | No |
Parameters
Parameters make templates reusable:
Parameters:
VpcCidr:
Type: String
Default: "10.0.0.0/16"
AllowedPattern: "(\\d{1,3}\\.){3}\\d{1,3}/\\d{1,2}"
Description: "VPC CIDR block"
DatabasePassword:
Type: String
NoEcho: true
MinLength: 8
Description: "Database password (hidden)"
| Parameter Property | Purpose |
|---|---|
Type |
String, Number, CommaDelimitedList, AWS::EC2::KeyPair::KeyName, etc. |
Default |
Value used if not provided |
AllowedValues |
Restrict to a list of options |
AllowedPattern |
Regex validation |
NoEcho |
Hide value in console (for secrets) |
MinLength / MaxLength |
String length constraints |
MinValue / MaxValue |
Number constraints |
Mappings
Static lookup tables — useful for region-specific values:
Mappings:
InstanceSize:
dev:
EC2: t3.micro
RDS: db.t3.micro
production:
EC2: t3.large
RDS: db.r5.large
Access with !FindInMap:
InstanceType: !FindInMap [InstanceSize, !Ref EnvironmentType, EC2]
Conditions
Create resources conditionally:
Conditions:
IsProduction: !Equals [!Ref EnvironmentType, production]
Resources:
ProductionAlarm:
Type: AWS::CloudWatch::Alarm
Condition: IsProduction
Properties:
AlarmName: "high-cpu-production"
# ... only created in production
Pseudo Parameters
Built-in parameters you don’t need to define:
| Pseudo Parameter | Value |
|---|---|
AWS::AccountId |
123456789012 |
AWS::Region |
ap-south-1 |
AWS::StackName |
Name of the current stack |
AWS::StackId |
ARN of the current stack |
AWS::NoValue |
Removes a property (use with conditions) |
Common Mistakes
| Mistake | Why It Hurts | Fix |
|---|---|---|
| Hard-coding AMI IDs | AMIs are region-specific | Use Mappings or SSM Parameter Store |
No NoEcho on passwords |
Password visible in console | Always use NoEcho: true for secrets |
Missing DependsOn |
Race conditions between resources | Use DependsOn or !Ref (implicit dependency) |
| Giant monolithic template | Hard to maintain, slow to update | Split into nested stacks |
Quick Reference
| Task | Command |
|---|---|
| Validate template | aws cloudformation validate-template --template-body file://template.yml |
| Create stack | aws cloudformation create-stack --stack-name NAME --template-body file://template.yml --parameters ... |
| Describe stack | aws cloudformation describe-stacks --stack-name NAME |
| Get template | aws cloudformation get-template --stack-name NAME |
Next → Intrinsic Functions