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:
- Go to AWS Console
- Click to create an EC2 instance
- Click to create an S3 bucket
- Click to create a Lambda function
- Click to create IAM roles
- Configure networking...
- Repeat for testing environment
- Repeat for production environment
- 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
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
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)
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
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]
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
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
!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
!FindInMap – Look Up Value from Mapping
!FindInMap [MapName, FirstKey, SecondKey]
Dynamic References: Keep Secrets Safe
The Problem
You don't want secrets hardcoded in templates:
Resources:
MyDB:
Properties:
Password: "MySecretPassword123"
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}}'
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}}'
How It Works
Template Parameter Store
{{resolve:ssm...}} → or Secret Manager → Value is resolved at
(fetches value) deployment time, NOT
stored in template
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:
- Stack update starts
- CloudFormation monitors CloudWatch alarm for 10 minutes
- If alarm triggers (errors spike) → Auto rollback
- Stack reverts to previous working state
- 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
Stack B (App Stack):
Resources:
MyEC2Instance:
Type: AWS::EC2::Instance
Properties:
VpcId: !ImportValue NetworkStack-VPCId
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
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:
- Login to account 1 → Deploy stack
- Login to account 2 → Deploy stack
- ... 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
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"
Flow:
- Stack create/update starts
- CloudFormation invokes your Lambda
- Lambda does custom work (API calls, etc.)
- Lambda signals success/failure to CloudFormation
- 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
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:
- Add resources to template with same configuration
- Use "Import resources into Stack" action
- Provide resource identifier (bucket name, table name)
- CloudFormation adopts the resources
After Import:
Stack now manages:
├── S3 Bucket
├── DynamoDB Table
└── Lambda Function
Future updates go through CloudFormation!
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
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!
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)
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)