DEV Community

Cover image for AWS CloudFormation: The Complete Beginner's Guide to Infrastructure as Code
Ege Pakten
Ege Pakten

Posted on

AWS CloudFormation: The Complete Beginner's Guide to Infrastructure as Code

Whether you're preparing for an AWS certification, starting your cloud journey, or just curious about how companies manage hundreds of servers without losing their minds this guide is for you. I'll explain everything in simple terms with real problems and solutions.


Key Concepts to Know First

Before we dive in, here are some terms you'll encounter:

  • Stack – A collection of AWS resources managed as one unit
  • Template – A YAML/JSON file that describes what resources to create
  • Resource – Any AWS service (EC2, S3, Lambda, etc.)
  • IAM – Identity and Access Management (controls who can do what)
  • VPC – Virtual Private Cloud (your private network in AWS)

What is CloudFormation?

CloudFormation = Infrastructure as Code

The Problem (Without CloudFormation)

Imagine you need to set up your application infrastructure:

  1. Go to AWS Console
  2. Click to create an EC2 instance
  3. Click to create an S3 bucket
  4. Click to create a Lambda function
  5. Click to create IAM roles
  6. Configure networking...
  7. Repeat for testing environment
  8. Repeat for production environment
  9. Repeat when you make a mistake

😰 This is slow, error-prone, and impossible to track changes.

The Solution (With CloudFormation)

Instead of clicking around, you define everything in a template file (YAML or JSON):

Resources:
  MyServer:
    Type: AWS::EC2::Instance
    Properties:
      InstanceType: t2.micro

  MyStorage:
    Type: AWS::S3::Bucket
Enter fullscreen mode Exit fullscreen mode

Then CloudFormation:

  • Creates all resources automatically
  • Creates them in the right order (handles dependencies)
  • Can recreate the exact same setup anytime
  • Tracks all changes

CloudFormation Template Structure

Every template has these sections:

AWSTemplateFormatVersion: '2010-09-09'    # Always this version

Description: Text description of what template does

Parameters:      # Input values (like variables), makes template reusable

Conditions:      # Controls whether resources are created

Resources:       # AWS resources to create (REQUIRED - only mandatory section)

Outputs:         # Values to export/display, can be imported by other stacks

Transform:       # Convert simplified code to full CloudFormation
                 # Most common use: AWS SAM
Enter fullscreen mode Exit fullscreen mode

What is AWS SAM?

SAM (Serverless Application Model) lets you write shorter templates for serverless apps. CloudFormation transforms it into full verbose syntax.

Think of it as a shortcut:

SAM (simple) → Transform → CloudFormation (full)
Enter fullscreen mode Exit fullscreen mode

Mappings: Your Static Lookup Table

The Problem

Different AWS regions have different AMI IDs (Amazon Machine Image). You don't want to hardcode them:

ImageId: ami-12345678
Enter fullscreen mode Exit fullscreen mode

This only works in us-east-1!

The Solution: Mappings

Mappings work like a dictionary—you look up values based on keys:

Mappings:
  RegionAMI:           # Map Name
    us-east-1:         # First Key
      AMI: ami-12345678    # Value
    eu-west-1:         # First Key
      AMI: ami-87654321    # Value

Resources:
  MyServer:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !FindInMap [RegionAMI, !Ref "AWS::Region", AMI]
Enter fullscreen mode Exit fullscreen mode

The FindInMap function takes: Map Name, First Key, Second Key

Now your template works in any region!

Key Point: Mappings are static (hardcoded). Use Parameters for dynamic input.


Metadata: Extra Information

Metadata provides extra information about your template or resources:

Group and organize data for better display in AWS Console

Remember placement in CloudFormation Designer (visual tool)

Tell EC2 what to install initially with
AWS::CloudFormation::Init

**
Example: Using Metadata to install packages on EC2 at launch
**


Resources:
  MyEC2Instance:
    Type: AWS::EC2::Instance
    Metadata:
      AWS::CloudFormation::Init:
        config:
          packages:
            yum:
              nginx: []
              php: []
          services:
            sysvinit:
              nginx:
                enabled: true
                ensureRunning: true
    Properties:
      InstanceType: t2.micro
      ImageId: ami-12345678

Enter fullscreen mode Exit fullscreen mode

This tells CloudFormation: "When this EC2 instance launches, automatically install nginx and php, then start the nginx service."

Without Metadata, you'd have to SSH into the server and install everything manually!

Intrinsic Functions: Your Helper Tools

These are built-in functions to reference values dynamically.

!Ref – Reference a Parameter or Resource

Parameters:
  BucketName:
    Type: String
    Default: my-bucket

Resources:
  MyBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Ref BucketName

  MyLambda:
    Properties:
      Environment:
        BUCKET: !Ref MyBucket
Enter fullscreen mode Exit fullscreen mode

!Ref BucketName returns "my-bucket", !Ref MyBucket returns the bucket name.

!GetAtt – Get Specific Attribute of a Resource

Sometimes you need more than just the name:

!Ref MyBucket                    # Returns: bucket name
!GetAtt MyBucket.Arn             # Returns: bucket ARN
!GetAtt MyBucket.DomainName      # Returns: bucket domain
Enter fullscreen mode Exit fullscreen mode

!FindInMap – Look Up Value from Mapping

!FindInMap [MapName, FirstKey, SecondKey]
Enter fullscreen mode Exit fullscreen mode

Dynamic References: Keep Secrets Safe

The Problem

You don't want secrets hardcoded in templates:

Resources:
  MyDB:
    Properties:
      Password: "MySecretPassword123"
Enter fullscreen mode Exit fullscreen mode

Anyone with template access can see your password!

The Solution: Dynamic References

Pull secrets from secure stores at deployment time:

Password: '{{resolve:ssm-secure:/myapp/db-password}}'
Enter fullscreen mode Exit fullscreen mode

The value is fetched at deployment, NOT stored in template.

Three Types of Dynamic References

Type Use Case Syntax
SSM Parameter Store (Plaintext) Non-sensitive config values {{resolve:ssm:/myapp/db-name}}
SSM Parameter Store (Secure) Sensitive values (encrypted) {{resolve:ssm-secure:/myapp/db-password}}
Secrets Manager Secrets with rotation & versioning {{resolve:secretsmanager:MyDBSecret}}

For Secrets Manager with specific JSON key:

Password: '{{resolve:secretsmanager:MyDBSecret:SecretString:password}}'
Enter fullscreen mode Exit fullscreen mode

How It Works

Template                    Parameter Store
{{resolve:ssm...}}    →    or Secret Manager    →    Value is resolved at
                           (fetches value)           deployment time, NOT
                                                     stored in template
Enter fullscreen mode Exit fullscreen mode

Stacks: Your Unit of Deployment

Stack = A collection of AWS resources managed as one unit

When you deploy a template, CloudFormation creates a Stack.

Stack Operations

Operation What Happens
Create Stack CloudFormation reads template, creates all resources in dependency order. If any resource fails → rollback all (delete everything)
Update Stack Modify template, update stack. Only changed resources are updated. Unchanged resources keep running
Delete Stack Deletes all resources in the stack. Clean removal of entire infrastructure

Change Sets: Preview Before You Deploy

The Problem

You want to update a stack but you're worried:

"If I change the instance type, will CloudFormation delete my database? Replace my server? What happens?"

The Solution: Change Sets

Change Set = Preview of what will happen before you update

Step 1: Upload new template (don't apply yet)

Step 2: CloudFormation creates Change Set showing:

Resource Action Replacement Details
MyEC2Instance Modify No Type change
MyS3Bucket Modify Yes ⚠️ Name change (will be deleted & recreated!)
NewLambda Add - New resource

Step 3: Review and decide

  • If OK → Execute Change Set
  • If not OK → Modify template and try again

Rollback Triggers: Automatic Safety Net

The Problem

You update your stack with new Lambda code. Deployment succeeds, but the new code has a bug! Error rate spikes to 50%.

Without Rollback Triggers:

  • You manually notice errors (could take hours)
  • You manually trigger rollback
  • Downtime continues while you react

The Solution: Rollback Triggers

Connect CloudFormation to CloudWatch Alarms:

Flow:

  1. Stack update starts
  2. CloudFormation monitors CloudWatch alarm for 10 minutes
  3. If alarm triggers (errors spike) → Auto rollback
  4. Stack reverts to previous working state
  5. No manual intervention needed!

Drift: When Reality Doesn't Match Template

What is Drift?

Drift = When actual resources DON'T match the template

Example:

Template Says But Actual Resource Has
IAM Role: S3 Read, DynamoDB Read IAM Role: S3 Read, DynamoDB Read, S3 Full Access ← DRIFT!
Lambda: 128MB Lambda: 128MB ✓
API: /users API: /users ✓

Someone manually added "S3 Full Access" in the console. This is unauthorized and doesn't match the template!

Drift Detection

CloudFormation can compare template vs actual resources:

Resource Type Status
MyLambdaFunction Lambda ✅ IN-SYNC
MyIAMRole IAM::Role ⚠️ MODIFIED
MyAPIGateway API Gateway ✅ IN-SYNC

Now you know exactly which resources were changed outside CloudFormation.


Stack Outputs & Cross-Stack References

The Problem

You have two stacks:

  • Stack A: Creates VPC and networking
  • Stack B: Creates EC2 instances that need the VPC ID

How does Stack B get the VPC ID from Stack A?

The Solution: Outputs & Exports

Stack A (Network Stack):

Outputs:
  VPCId:
    Value: !Ref MyVPC
    Export:
      Name: NetworkStack-VPCId
Enter fullscreen mode Exit fullscreen mode

Stack B (App Stack):

Resources:
  MyEC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      VpcId: !ImportValue NetworkStack-VPCId
Enter fullscreen mode Exit fullscreen mode

Nested Stacks: Organize Large Templates

The Problem

Your template is 5000 lines long and hard to manage. Everything is in one massive file.

The Solution: Nested Stacks

Nested Stacks = Stacks within Stacks (like folders in folders)

Break into smaller, manageable pieces:

Parent Stack (main.yaml):

Resources:
  NetworkStack:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: s3://bucket/network.yaml

  DatabaseStack:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: s3://bucket/database.yaml

  AppStack:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: s3://bucket/app.yaml
Enter fullscreen mode Exit fullscreen mode

Now each component is separate and reusable!


StackSets: Deploy Across Multiple Accounts/Regions

The Problem

Your company has 50 AWS accounts (dev, prod, per team). You need to deploy a security baseline to ALL accounts.

Without StackSets:

  1. Login to account 1 → Deploy stack
  2. Login to account 2 → Deploy stack
  3. ... repeat 48 more times 😫

The Solution: StackSets

StackSets = Deploy same stack to multiple accounts/regions with one click

Administrator Account
├── Stack Set: "SecurityBaseline"
├── Template: security-baseline.yaml
├── Target Accounts: [111111, 222222, 333333]
├── Target Regions: [us-east-1, eu-west-1]
└── One Click → Deploy to all!
         │
         ├── Account 1 (us-east-1) ✓ Stack deployed
         ├── Account 2 (us-east-1) ✓ Stack deployed
         └── Account 3 (us-east-1) ✓ Stack deployed
Enter fullscreen mode Exit fullscreen mode

Key Concepts

Term Meaning
Administrator Account Where you CREATE the StackSet
Target Accounts Where stacks are deployed
Stack Instances References to stacks in target accounts

Use Cases

  • Security baselines across organization
  • Compliance rules in all accounts
  • Centralized logging setup

Custom Resources: When CloudFormation Isn't Enough

The Problem

CloudFormation doesn't support everything. What if you need to:

  • Call a third-party API during deployment?
  • Run a custom script?
  • Create a resource CloudFormation doesn't support?

The Solution: Custom Resources backed by Lambda

Resources:
  MyCustomResource:
    Type: Custom::MyCustomThing
    Properties:
      ServiceToken: !GetAtt MyLambda.Arn
      CustomParam1: "Value1"
Enter fullscreen mode Exit fullscreen mode

Flow:

  1. Stack create/update starts
  2. CloudFormation invokes your Lambda
  3. Lambda does custom work (API calls, etc.)
  4. Lambda signals success/failure to CloudFormation
  5. Stack continues or rolls back

CloudFormation Designer

Visual drag-and-drop tool for templates

Instead of writing YAML by hand, you can:

  • Drag and drop resources visually
  • See relationships between resources
  • Generate template code automatically

Great for learning and visualizing architecture!


Modules and Macros

Modules: Reusable Building Blocks

Problem: Same resource pattern repeated across templates. Every Lambda needs:

  • Lambda Function
  • IAM Role
  • CloudWatch Log Group
  • Alarm for errors

Solution: Create a Module!

Resources:
  MyLambdaModule:
    Type: MyCompany::Lambda::StandardFunction
    Properties:
      FunctionName: ProcessOrders
      Runtime: python3.9
Enter fullscreen mode Exit fullscreen mode

The module creates Lambda + Role + Logs + Alarm automatically!

Macros: Transform Templates

Macros = Transform templates before processing

  • Macros process your template through Lambda
  • Can do find-replace, add resources, transform syntax

Example: AWS::Serverless Transform (SAM)

  • You write simplified SAM syntax
  • Macro transforms it to full CloudFormation

Resource Import: Bring Existing Resources Into a Stack

The Problem

You have resources created manually (not in a stack):

  • S3 bucket
  • DynamoDB table
  • Lambda function

Now you want CloudFormation to manage them.

The Solution: Resource Import

Steps:

  1. Add resources to template with same configuration
  2. Use "Import resources into Stack" action
  3. Provide resource identifier (bucket name, table name)
  4. CloudFormation adopts the resources

After Import:

Stack now manages:
├── S3 Bucket
├── DynamoDB Table
└── Lambda Function

Future updates go through CloudFormation!
Enter fullscreen mode Exit fullscreen mode

Deletion Policy: What Happens When Resources Are Deleted?

When you delete a stack or remove a resource, what happens to the data?

Three Options

Policy What Happens Use Case
Delete (Default) Resource is deleted when removed from stack Temporary resources
Retain Resource is kept even when removed from stack. CloudFormation stops managing it, but doesn't delete Important resources you might need
Snapshot Creates backup before deleting (RDS, EBS volumes) Databases you want backed up

Example:

Resources:
  MyDatabase:
    Type: AWS::RDS::DBInstance
    DeletionPolicy: Snapshot
Enter fullscreen mode Exit fullscreen mode

This creates a backup before deleting the database.


Stack Failure Options

The Problem

What happens when a resource fails to create?

Default behavior: Stack creation fails → Rollback all (delete everything created)

But sometimes you want to keep successful resources for debugging.

The Solution: Preserve Successfully Provisioned Resources

Stack Creates:
1. VPC ✓
2. Subnet ✓
3. EC2 Instance ✗ (fails!)

Default: All deleted
New Option: VPC and Subnet kept for debugging!
Enter fullscreen mode Exit fullscreen mode

Benefits:

  • Troubleshoot why EC2 failed
  • Don't waste time recreating successful resources
  • Fix and retry without restarting

CloudFormation Security

IAM Permissions

Control who can:

  • Create Stacks
  • Update Stacks
  • Delete Stacks
  • View Templates

Service Roles

Without Service Role:

  • User's permissions used → User needs ALL resource permissions

With Service Role:

  • Role's permissions used → User only needs CloudFormation permissions
User (Limited)  →  CloudFormation  →  Resources (Created)
                   (Assume Role)
                        ↓
                   Service Role
                   (Full Perms)
Enter fullscreen mode Exit fullscreen mode

This is more secure—users don't need direct access to resources!

VPC Endpoints (PrivateLink)

  • Access CloudFormation API without going through internet
  • All traffic stays within AWS network
  • More secure for sensitive environments

Quick Reference: All Concepts at a Glance

Concept One-Line Description
Template YAML/JSON file defining your infrastructure
Stack Collection of resources managed as one unit
Parameters Input variables for templates
Mappings Static lookup tables (like dictionaries)
Outputs Values exported for other stacks to use
!Ref Reference a parameter or resource
!GetAtt Get specific attribute of a resource
!FindInMap Look up value from Mapping
Dynamic References Pull secrets from secure stores
Change Sets Preview changes before applying
Drift Detection Find resources changed outside CloudFormation
Nested Stacks Stacks within stacks for organization
StackSets Deploy to multiple accounts/regions
Custom Resources Run custom code during deployment
Modules Reusable resource patterns
Macros Transform templates before processing
Resource Import Bring existing resources into a stack
Deletion Policy Control what happens when resources deleted
Rollback Triggers Auto-rollback based on CloudWatch alarms
Service Roles Let CloudFormation assume role for permissions

I'm at the very beginning of my AWS journey, and I tried my best to explain this concept in my own words. If you have any additional information, corrections, or insights about CloudFormation, please share in the comments—I'd be happy to learn from you as well!

Top comments (0)