4.1.3. CloudFormation: Templates, Stacks, and StackSets
💡 First Principle: CloudFormation treats infrastructure as a state machine. You declare the desired end state in a template, and CloudFormation figures out how to get there — creating, updating, or deleting resources in the right order, handling dependencies automatically. This is fundamentally different from scripting (where you tell it how to do it) — CloudFormation lets you describe what you want.
Without CloudFormation, provisioning a three-tier application means: create VPC, create subnets, create security groups, create RDS, create EC2, configure ELB — in the right order, with the right parameters. Make a mistake and you have to manually undo it. With CloudFormation, you declare all of that in one template, and CloudFormation handles creation, ordering, rollback on failure, and deletion.
Template Anatomy:
| Section | Required? | Purpose |
|---|---|---|
AWSTemplateFormatVersion | No | Always 2010-09-09 |
Description | No | Human-readable description |
Parameters | No | Input values at deploy time |
Mappings | No | Static lookup tables (e.g., AMI IDs by region) |
Conditions | No | Conditional resource creation |
Resources | ✅ Yes | The actual AWS resources to create |
Outputs | No | Values to export or display after creation |
Transform | No | Macros, SAM transforms |
Stack Operations:
| Operation | What Happens |
|---|---|
| Create | Provisions all resources; rolls back on failure |
| Update | Changes are calculated via change set; existing resources updated, added, or deleted |
| Delete | All resources in the stack are deleted (unless DeletionPolicy is set) |
| Detect Drift | Compares actual resource configuration to template definition |
Change Sets: Before executing an update, create a change set to preview exactly which resources will be added, modified, or deleted. This prevents surprise deletions — particularly critical for databases and stateful resources.
DeletionPolicy: Controls what happens to a resource when its stack is deleted or the resource is removed from the template:
| Value | Effect |
|---|---|
Delete (default) | Resource is deleted |
Retain | Resource is kept (orphaned from stack) |
Snapshot | A final snapshot is taken before deletion (RDS, EBS, ElastiCache) |
Other protections: Termination protection is a stack setting that blocks deleting the whole stack; a stack policy denies updates to the resources it names. Neither previews an update — only a change set does — and neither keeps one resource when the stack is deleted (that is DeletionPolicy).
Ordering and attributes: CloudFormation creates independent resources in parallel, and their order in the template has no effect. A !Ref or !GetAtt to another resource creates an implicit dependency; DependsOn adds an explicit one, so the dependent resource starts only after the other reaches CREATE_COMPLETE. !Ref returns a resource's default identifier (an instance ID for AWS::EC2::Instance), while !GetAtt returns a named attribute such as !GetAtt WebServer.PrivateIp.
UPDATE_ROLLBACK_FAILED: The rollback itself failed, typically because a resource was changed or deleted outside CloudFormation (or permissions were missing). Fix the cause, then run ContinueUpdateRollback, which can skip resources that can no longer be restored — no need to delete the stack.
CloudFormation Helper Scripts run inside EC2 instances to integrate instance initialization with stack creation:
| Script | Purpose |
|---|---|
cfn-init | Reads AWS::CloudFormation::Init metadata and applies it (installs packages, writes files, runs commands) |
cfn-signal | Signals the stack that initialization succeeded or failed (used with CreationPolicy/WaitCondition) |
cfn-hup | Daemon that detects metadata changes and re-runs cfn-init on stack updates |
cfn-get-metadata | Retrieves resource metadata |
CreationPolicy and WaitCondition: These tell CloudFormation to wait for a signal before marking a resource as successfully created. Without them, CloudFormation marks an EC2 instance as created the moment the API call succeeds — before the instance has actually finished its initialization script. A CreationPolicy counts cfn-signal calls from the instances themselves (cfn-init finishing is not a signal); a WaitCondition waits for a signal sent to the pre-signed URL of its WaitConditionHandle.
Resources:
WebServer:
Type: AWS::EC2::Instance
CreationPolicy:
ResourceSignal:
Timeout: PT15M # Wait up to 15 minutes for signal
Count: 1
Metadata:
AWS::CloudFormation::Init:
config:
packages:
yum:
httpd: []
commands:
start_apache:
command: "systemctl start httpd"
Drift Detection: Identifies configuration changes made outside CloudFormation (console changes, CLI changes, AWS Config remediations). Running drift detection shows you which resources have drifted and what changed. It does not automatically remediate drift — you must decide whether to update the template or revert the resource.
Nested Stacks and Cross-Stack References:
| Pattern | Use Case |
|---|---|
| Nested Stacks | Reuse common templates (VPC, security groups) across multiple parent stacks |
| Cross-Stack References | Export output from Stack A (e.g., VPC ID) and import it in Stack B |
| StackSets | Deploy the same template to multiple accounts and/or regions simultaneously |
StackSets are critical for multi-account deployments. From the management account (or a delegated admin account), you define a StackSet and target it at OUs or account lists. CloudFormation deploys the stack in every targeted account and region automatically.
⚠️ Exam Trap: CloudFormation cannot update a resource that requires replacement (e.g., changing an RDS instance's DB engine) without deleting and recreating it. Changing certain properties triggers replacement, which means downtime for stateful resources. Always check the "Update requires" field in the CloudFormation documentation before updating production stacks, and use DeletionPolicy: Snapshot to protect data.
Reflection Question: A CloudFormation stack creates an EC2 instance. The stack creation completes successfully in the console, but the application on the instance isn't working because the bootstrap script failed silently. What CloudFormation mechanism should have been used to catch this, and how does it work?