AWS for DevOps — CloudFormation Templates

2 minute read

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

Updated: