CloudFormation — Cheat Sheet (DVA-C02)
Verified against AWS documentation on 2026-10-12 · ⚠️ = changed vs older material: learn the value shown here (older practice material may say otherwise; full list in notes/cloudformation-guide.md#changed-vs-older-material) · ⚠️ unverified = not confirmed in current AWS docs · explanations and examples: notes/cloudformation-guide.md.
Stacks and the deployment workflow
| Fact | Current value | Memory hook |
|---|---|---|
| Template size | ⚠️ Inline --template-body up to 51,200 bytes; S3 --template-url up to 1 MB; S3 needed only above 51,200 bytes |
50 KB inline, 1 MB in S3 |
| Template quotas | ⚠️ 500 resources · 200 parameters · 200 outputs · 200 mappings (200 attributes each) · 60 dynamic references | 5-2-2-2 |
| Stacks per Region | 2,000 (adjustable); REVIEW_IN_PROGRESS stacks count |
|
| Stack name | Up to 128 characters, letters, digits, hyphens, starts with a letter, unique per Region | |
| Tags and notifications | Up to 50 stack tags (propagated to resources) · up to 5 SNS topics in NotificationARNs |
|
ROLLBACK_COMPLETE |
Failed create; only delete is allowed → delete and recreate | Dead on arrival |
UPDATE_COMPLETE_CLEANUP_IN_PROGRESS |
Update succeeded; replaced resources being deleted; stack usable | Sweeping up |
| Replacement | New resource first, references switched, old one deleted in cleanup; custom-named resources can't be replaced | Build, switch, demolish |
| Lambda code in S3 | Same S3Key = no change detected → new key or S3ObjectVersion (package uses checksum keys) |
New name, new code |
package and deploy |
package uploads local artifacts and rewrites the template; deploy = create + execute a change set (--no-execute-changeset, --parameter-overrides, --s3-bucket above 51,200 bytes) |
Pack, then ship |
validate-template |
JSON/YAML syntax only; returns parameters, capabilities, transforms | Spell-check, not fact-check |
| Visual editor | ⚠️ Template studio in the console; standalone Infrastructure Composer console ends support 2026-12-07 | |
| Express mode | ⚠️ --deployment-config '{"mode": "EXPRESS"}': no stabilization wait, rollback off by default, no StackSets; development only |
Fast lane, no airbag |
| Resource type count | ⚠️ unverified (course: "over 700"; no current official figure) |
Template sections and parameters
| Fact | Current value | Memory hook |
|---|---|---|
| Required section | Only Resources |
No resources, no stack |
AWSTemplateFormatVersion |
Only 2010-09-09; literal; optional |
|
| Optional sections | Description (up to 1,024 bytes), Metadata, Parameters, Rules, Mappings, Conditions, Transform, Outputs |
|
| Parameter types | String, Number (Ref returns a string), List<Number>, CommaDelimitedList, AWS-specific, SSM types |
|
| Parameter constraints | AllowedValues, AllowedPattern, MinLength/MaxLength, MinValue/MaxValue, ConstraintDescription, Default, NoEcho |
|
| No default and no value | Parameters: [Name] must have values |
|
| AWS-specific types | Value must name a resource that already exists in the deploying account and Region (AWS::EC2::VPC::Id, List<AWS::EC2::Subnet::Id>…); other accounts → String |
Must already exist here |
| SSM parameter types | Name in, latest value out at every create, update and change set; UsePreviousValue keeps the key; shared parameter → full ARN; no SecureString type |
Name in, latest out |
NoEcho |
Masked in describe calls; not masked when copied into Outputs or Metadata |
A mask, not a vault |
Rules |
RuleCondition + Assertions (Assert, AssertDescription) with rule functions such as Fn::Contains; fail before any resource changes |
Bouncer at the door |
YAML Yes/No |
Quote them in AllowedValues |
|
Transform |
Macros (SAM, AWS::Include, AWS::LanguageExtensions) → deploy through a change set or with CAPABILITY_AUTO_EXPAND |
Mappings, conditions and outputs
| Fact | Current value | Memory hook |
|---|---|---|
| Mappings | Two-level static tables; literal keys; string or list values; no functions inside | Phone book, not a calculator |
Fn::FindInMap |
[Map, TopKey, SecondKey]; keys may use Ref or nested FindInMap; ⚠️ DefaultValue fallback works in any template |
|
| Mapping vs parameter | Mapping = known values keyed by Region or environment; parameter = user input; latest AMI → Parameter Store | |
| Condition functions | Fn::Equals, Fn::And and Fn::Or (2–10 conditions), Fn::Not, Fn::If, Condition: |
|
| Condition inputs | Parameters, pseudo parameters, mappings, other conditions; never resource attributes | |
| Applying conditions | Condition on resources and outputs; Fn::If in properties, outputs, Metadata, UpdatePolicy; AWS::NoValue removes a property |
|
| Condition turns false | The resource is deleted on that update (its DeletionPolicy applies) |
False = gone |
| Edits not seen as an update | Only conditions, outputs, DeletionPolicy or UpdatePolicy → also change a resource (e.g. its Metadata) |
|
| Outputs | Up to 200; Value required; optional Export and Condition; never redacted; unavailable during *_IN_PROGRESS |
Shop window, no secrets |
Intrinsic functions and pseudo parameters
| Fact | Current value | Memory hook |
|---|---|---|
Ref on a resource |
Type-specific ID: instance ID, bucket name, queue URL, topic ARN, function name, role name, table name, EIP address | Ref = ID card |
| Function name | ⚠️ Ref (JSON {"Ref": "X"}, YAML !Ref X); there is no Fn::Ref |
No prefix for Ref |
Fn::GetAtt |
Other attributes: .Arn, .DNSName, .StreamArn, Child.Outputs.Name for nested stacks |
GetAtt = details |
Fn::Sub |
${Param}, ${Resource}, ${Resource.Attr}, ${AWS::Region}; ${!Literal} writes a literal |
|
Fn::GetAZs "" |
Availability Zones of the current Region, alphabetical (only default-subnet AZs when any exist) | |
Fn::Base64 |
Encodes EC2 UserData; write Fn::Base64: !Sub | |
|
| Short-form YAML | No short form directly inside another (!Base64 !Sub, !GetAZs !Ref, !ImportValue !Sub are invalid) |
One bang at a time |
| Language extensions | Fn::ForEach, Fn::Length, Fn::ToJsonString need Transform: AWS::LanguageExtensions; full names only |
|
| Pseudo parameters | AWS::AccountId, AWS::Region, AWS::StackName, AWS::StackId, AWS::Partition, AWS::URLSuffix, AWS::NotificationARNs, AWS::NoValue |
|
| Portable ARNs | arn:${AWS::Partition}:service:${AWS::Region}:${AWS::AccountId}:… |
Exports and nested stacks
| Fact | Current value | Memory hook |
|---|---|---|
| Export names | Unique per account per Region | |
Fn::ImportValue scope |
Same account and Region only | |
| Export in use | Exporting stack can't be deleted and the output can't change → list-imports, migrate consumers first |
Can't pull the rug |
| Export name functions | Fn::Sub and similar allowed; no Ref/GetAtt on resources |
|
| Nested stacks | AWS::CloudFormation::Stack with an S3 TemplateURL; parent reads !GetAtt Child.Outputs.Name |
Russian dolls |
| Nested operations | Update, change set and delete from the root; termination protection set on the root; up to 2,500 resources per operation | Steer from the top |
| Exports vs nested | Independent stacks sharing values → exports; reusable component or more than 500 resources → nested stacks | |
Fn::GetStackOutput |
⚠️ Outputs from other Regions or accounts (StackName, OutputName, Region, RoleArn with cloudformation:DescribeStacks); no export; weak reference |
Phone call, not a contract |
Service roles and capabilities
| Fact | Current value | Memory hook |
|---|---|---|
| Without a service role | CloudFormation uses a temporary session from the caller's credentials | |
| Service role | Trusts cloudformation.amazonaws.com; set with --role-arn; used for all later operations; can be replaced, not removed |
|
| Attaching the role | Needs iam:PassRole on it (iam:PassedToService = cloudformation.amazonaws.com) |
No pass, no deploy |
| Escalation risk | Anyone allowed to operate the stack acts with its role | |
| Condition keys | cloudformation:RoleARN, TemplateUrl, StackPolicyUrl, ResourceTypes, ImportResourceTypes, ChangeSetName (create and update); they test request parameters, so a RoleARN condition denies calls without --role-arn, even on a stack that already has the role |
|
aws:SourceIp |
Don't use it: CloudFormation calls services from its own addresses | |
CAPABILITY_IAM |
IAM resources with generated names | |
CAPABILITY_NAMED_IAM |
Any IAM resource with a custom name (RoleName, UserName…) |
Named → NAMED |
CAPABILITY_AUTO_EXPAND |
⚠️ Macros or transforms on a direct create or update; nested stacks alone don't need it | |
| Missing capability | InsufficientCapabilities, HTTP 400 (CLI: InsufficientCapabilitiesException) |
|
| Named IAM in several Regions | Collides (IAM names are account-wide) → add ${AWS::Region} or let CloudFormation generate names |
Change sets
| Fact | Current value | Memory hook |
|---|---|---|
| Purpose | Preview add, modify, remove, import; nothing changes until execute-change-set |
Look before you leap |
| Types | UPDATE (default), CREATE (stack in REVIEW_IN_PROGRESS), IMPORT |
|
Replacement |
True, False or Conditional (may replace; decided at execution) |
Conditional = maybe |
| After executing one | All other change sets of the stack are deleted | |
| No differences | The change set ends FAILED |
|
| Success guarantee | None; ⚠️ pre-deployment validation: property syntax and name conflicts fail early on create, update and change set; change sets also warn on quotas, non-empty buckets, Config recorders, ECR repositories; --disable-validation skips it |
|
| Drift-aware change set | ⚠️ --deployment-mode REVERT_DRIFT: compares actual state and reverts drift (not AWS-managed, write-only or immutable properties) |
|
| Nested stacks | CLI needs --include-nested-stacks (console default on); execute or delete from the root |
|
| Parameter Store values | Read when the change set is created, not when it is executed | |
| IAM | cloudformation:CreateChangeSet, ExecuteChangeSet; limit with cloudformation:ChangeSetName |
Rollbacks and stack failure options
| Fact | Current value | Memory hook |
|---|---|---|
OnFailure on create |
ROLLBACK (default), DO_NOTHING, DELETE; --disable-rollback = DO_NOTHING |
|
| Preserve resources | --disable-rollback on create, update, execute-change-set, deploy → CREATE_FAILED/UPDATE_FAILED → Retry, Update or rollback-stack; not for replacing updates |
Freeze frame |
--retain-except-on-create |
⚠️ Rollback deletes resources the failed operation created, even with Retain |
|
UPDATE_ROLLBACK_FAILED |
Fix the cause, then continue-update-rollback with --resources-to-skip (rollback failures only) or ⚠️ --force-rollback |
Skip the casualties |
| Rollback triggers | Up to 5 CloudWatch alarms (metric or composite); monitoring 0–180 minutes (default 0); only ALARM triggers |
Tripwires |
cancel-update-stack |
Only in UPDATE_IN_PROGRESS; the update rolls back |
|
DELETE_FAILED |
Fix and retry, --retain-resources, or ⚠️ --deletion-mode FORCE_DELETE_STACK; kept resources show DELETE_SKIPPED |
|
| Express mode rollback | Off by default; "disableRollback": false keeps it |
Protecting resources and stacks
| Fact | Current value | Memory hook |
|---|---|---|
DeletionPolicy |
Stack deleted, resource removed or condition false: Delete, Retain, Snapshot, ⚠️ RetainExceptOnCreate |
After the funeral |
Default DeletionPolicy |
⚠️ Delete, except Snapshot for AWS::RDS::DBCluster and AWS::RDS::DBInstance without DBClusterIdentifier |
RDS keeps a photo |
Snapshot support |
EBS volume, ElastiCache cluster and replication group, RDS instance and cluster, Redshift, Neptune and DocumentDB clusters | |
UpdateReplacePolicy |
Old resource after a replacement: Delete (default), Retain, Snapshot |
After the rebuild |
| Stack policy | Update:Modify, Update:Replace, Update:Delete, Update:*; Principal: "*"; once set, deny by default; explicit Deny wins; one per stack; can't be removed |
Guards updates |
| Stack policy override | --stack-policy-during-update-body for one update; set-stack-policy to change it |
|
| Termination protection | Blocks deleting the stack only; off by default; update-termination-protection; set on the root, inherited by nested stacks |
Guards deletes |
| Non-empty S3 bucket | Delete fails → empty it first (custom resource) or Retain it |
Custom resources
| Fact | Current value | Memory hook |
|---|---|---|
| Declaration | Custom::Name (up to 60 characters) or AWS::CloudFormation::CustomResource |
|
ServiceToken |
Lambda function or SNS topic ARN in the same Region as the stack | |
| Request | RequestType Create, Update or Delete, plus a presigned S3 ResponseURL |
|
| Response | Status, PhysicalResourceId, StackId, RequestId, LogicalResourceId, Reason (with FAILED), Data, NoEcho; body up to 4,096 bytes |
|
New PhysicalResourceId |
Treated as replacement → CloudFormation sends Delete for the old ID |
New ID = new thing |
| Timeout | ⚠️ ServiceTimeout 1–3,600 seconds (default 3,600) |
|
| Respond on every path | Errors and Delete included, or the stack waits until the timeout |
Always call back |
cfn-response module |
Only for inline ZipFile code (Node.js, Python); ⚠️ inline package up to 4 MB |
|
| Secure references | ssm-secure and secretsmanager references not supported in custom resource properties |
|
| Registry resource types | Alternative with create, read, update, delete, list and drift support |
Helper scripts and creation policies
| Fact | Current value | Memory hook |
|---|---|---|
| Helper scripts | cfn-init (apply metadata), cfn-signal (report), cfn-hup (watch for updates), cfn-get-metadata; package aws-cfn-bootstrap, /opt/aws/bin on Amazon Linux |
Init, Signal, Hup |
cfn-init order |
packages → groups → users → sources → files → commands → services; commands alphabetical; configSets to reorder |
|
CreationPolicy |
AWS::EC2::Instance, AWS::AutoScaling::AutoScalingGroup, AWS::CloudFormation::WaitCondition; Count 1 and Timeout PT5M by default, 12 h max |
|
| Signal in user data | cfn-signal -e $? --stack … --resource … --region … right after cfn-init; --region defaults to us-east-1 |
Exit code is the message |
| Helper credentials | None needed (stack membership check); with explicit ones: cloudformation:SignalResource or DescribeStackResource |
|
| Network path | Instance must reach the CloudFormation endpoint (and S3 for wait condition handles) | |
| Rolling update | AutoScalingRollingUpdate + WaitOnResourceSignals: true; PauseTime up to PT1H; MaxBatchSize 1, MinInstancesInService 0 by default |
|
cfn-hup |
/etc/cfn/cfn-hup.conf (interval default 15 minutes) + /etc/cfn/hooks.d/ hooks with triggers=post.update |
|
| Wait conditions | External coordination; Timeout up to 43,200 s; prefer CreationPolicy for EC2 and Auto Scaling |
|
| Logs | /var/log/cfn-init.log, /var/log/cloud-init.log, /var/log/cloud-init-output.log |
StackSets
| Fact | Current value | Memory hook |
|---|---|---|
| Model | One template → stack instances in many accounts and Regions from an administrator account; the StackSet itself is regional | |
| Self-managed roles | AWSCloudFormationStackSetAdministrationRole (administrator account) + AWSCloudFormationStackSetExecutionRole (every target) |
Admin calls, execution acts |
| Service-managed | Organizations trusted access; roles created for you; OU targets; automatic deployment; never the management account | |
| Delegated administrators | Up to 5 member accounts; --call-as DELEGATED_ADMIN |
|
| Service-managed limits | No macros, transforms or nested stacks | |
| Operation preferences | Max concurrent (count or %), failure tolerance (count or % per Region), Regions SEQUENTIAL (default) or PARALLEL, Region order |
|
| Concurrency mode | ⚠️ STRICT_FAILURE_TOLERANCE (default; at most tolerance + 1) or SOFT_FAILURE_TOLERANCE |
|
| Deleting | Stack instances first (--retain-stacks keeps the stacks), then the StackSet |
Drift detection and resource import
| Fact | Current value | Memory hook |
|---|---|---|
| Drift flow | detect-stack-drift → describe-stack-drift-detection-status → describe-stack-resource-drifts |
Detect, poll, read |
| Drift statuses | Stack DRIFTED, IN_SYNC, NOT_CHECKED; resource MODIFIED, DELETED, IN_SYNC, NOT_CHECKED |
|
| What is compared | Only properties set explicitly; not KMSKeyId, write-only values, Lambda code or dynamic references |
Only what you wrote |
| Scope | Nested stacks excluded (run on each); drift detection only reports | |
| Continuous drift checks | AWS Config managed rule cloudformation-stack-drift-detection-check (periodic and on changes) |
|
| Allowed stack states | CREATE_COMPLETE, UPDATE_COMPLETE, UPDATE_ROLLBACK_COMPLETE, UPDATE_ROLLBACK_FAILED |
|
| Resource import | --change-set-type IMPORT --resources-to-import; DeletionPolicy required; no other changes in the same operation |
|
| Auto-import | --import-existing-resources: static custom name plus Retain or RetainExceptOnCreate |
|
| Moving a resource | Retain → remove from the source stack → import into the target (or stack refactoring) |
Dynamic references
| Fact | Current value | Memory hook |
|---|---|---|
ssm |
{{resolve:ssm:name}} or :version; String/StringList; latest version when omitted |
|
ssm-secure |
{{resolve:ssm-secure:name}}; only supported properties (RDS and Redshift MasterUserPassword, IAM LoginProfile.Password…); plaintext never stored |
|
secretsmanager |
{{resolve:secretsmanager:id:SecretString:key:stage:version}}; default AWSCURRENT; any property; other account → full ARN |
|
| Refresh | Only when the resource holding the reference is created or updated | Read once, not watched |
| Not supported | Secure values in custom resources; AWS::CloudFormation::Init; EC2 UserData; trailing backslash |
|
| Permissions | ssm:GetParameters; secretsmanager:GetSecretValue |
Numbers to know
| Item | Value | Note |
|---|---|---|
| Inline template body | 51,200 bytes | Larger → S3 URL |
| Template in S3 | 1 MB ⚠️ | Older material: 450 KB |
| Resources per template | 500 ⚠️ | Older material: 200; split with nested stacks |
| Parameters per template | 200 ⚠️ | Older material: 60 |
| Outputs per template | 200 ⚠️ | Older material: 60 |
| Mappings per template | 200 ⚠️ | 200 attributes per mapping; older material: 100 mappings |
| Dynamic references per template | 60 | |
| Template description | 1,024 bytes | Literal string |
| Parameter value | 4,096 bytes | |
| Parameter description | 4,000 characters | |
| Logical IDs and names | 255 characters | |
| Stack name | 128 characters | |
| Stacks per Region | 2,000 | Adjustable |
| Stack tags | 50 | Propagated to resources |
| SNS notification topics per stack | 5 | NotificationARNs |
| Deleted stacks listed | 90 days | |
Fn::And / Fn::Or conditions |
2–10 | |
| Resources per nested-stack operation | 2,500 | Across the hierarchy |
| Rollback triggers | 5 alarms | |
| Rollback monitoring time | 0–180 minutes | Default 0 |
| Custom resource response | 4,096 bytes | |
Custom resource ServiceTimeout |
1–3,600 s ⚠️ | Default 3,600 |
| Custom resource type name | 60 characters | |
Inline ZipFile package |
4 MB ⚠️ | Older material: 4,096 characters |
CreationPolicy Count |
1 | Default |
CreationPolicy Timeout |
PT5M default, 12 h max |
|
MinSuccessfulInstancesPercent |
100 | Default |
Rolling update PauseTime |
Up to PT1H |
PT5M default with signals |
Rolling update MaxBatchSize |
1 | Default |
WaitCondition Timeout |
Up to 43,200 s | 12 hours |
cfn-hup interval |
15 minutes | Default |
cfn-signal data |
4,096 bytes | |
| Stack policy | 16 KB | |
| StackSet delegated administrators | 5 | |
| StackSets per administrator account | 1,000 | Adjustable |
| Stack instances per StackSet | 100,000 | Adjustable |
AWSTemplateFormatVersion |
2010-09-09 |
Only valid value |
Decision rules
Stacks, templates and functions
- If you see a template larger than 51,200 bytes, think S3
--template-url(up to 1 MB) ordeploy --s3-bucket. - If you see a Lambda function that keeps old code after a stack update, think a new
S3Key/S3ObjectVersionoraws cloudformation package. - If you see a stack in
ROLLBACK_COMPLETE, think delete and recreate. - If you see "latest AMI in every Region without template edits", think an
AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>parameter or a{{resolve:ssm:…}}reference. - If you see "validate parameter combinations before anything is created", think the
Rulessection. - If you see "this resource only in prod", think a condition and the
Conditionattribute. - If you see an IAM policy that needs a queue ARN, think
!GetAtt Queue.Arn(Refis the URL).
Sharing, permissions and change control
- If you see independent stacks that must share a VPC ID safely, think
ExportandFn::ImportValue. - If you see a reusable component or more than 500 resources, think nested stacks.
- If you see an output needed from another account or Region, think
Fn::GetStackOutput. - If you see "export cannot be deleted because it is in use", think
list-imports, migrate the consumers, then delete. - If you see developers deploying resources they can't create directly, think a service role plus
iam:PassRole. - If you see
InsufficientCapabilitieswith a named IAM resource, thinkCAPABILITY_NAMED_IAM; with a transform oncreate-stack, thinkCAPABILITY_AUTO_EXPAND. - If you see "preview whether an update replaces the database", think a change set and its
Replacementvalue.
Failures and protection
- If you see "keep what worked and retry after fixing", think
--disable-rollback. - If you see
UPDATE_ROLLBACK_FAILED, thinkcontinue-update-rollback(--resources-to-skipor--force-rollback). - If you see
DELETE_FAILEDon a non-empty bucket, think empty it,--retain-resourcesorFORCE_DELETE_STACK. - If you see "roll back when the error alarm fires after the deployment", think rollback triggers.
- If you see "keep the data when the stack is deleted", think
DeletionPolicy: RetainorSnapshot. - If you see "keep the old resource when an update replaces it", think
UpdateReplacePolicy. - If you see "no update may replace one critical resource", think a stack policy with an explicit
Deny. - If you see "prevent accidental stack deletion", think termination protection.
Extensions, accounts and secrets
- If you see a step CloudFormation can't do natively, think a Lambda-backed custom resource.
- If you see a stack stuck for an hour on a custom resource, think a missing response; fix the error handling and set
ServiceTimeout. - If you see "wait until the software is installed", think
CreationPolicypluscfn-signal -e $?. - If you see "running instances must pick up metadata changes", think
cfn-hupwith apost.updatehook. - If you see the same baseline in all accounts, including future ones, think a service-managed StackSet with automatic deployment.
- If you see "which resources were changed outside CloudFormation", think
detect-stack-drift. - If you see "bring a hand-made resource under a stack", think an
IMPORTchange set with aDeletionPolicy. - If you see a database password that must stay out of the template, think
{{resolve:secretsmanager:…}}.
Memory hooks
- No resources, no stack:
Resourcesis the only required section. - 50 KB inline, 1 MB in S3.
- 5-2-2-2: 500 resources, 200 parameters, 200 outputs, 200 mappings.
ROLLBACK_COMPLETEis dead on arrival: delete and recreate.- Same key, same code: rename the zip to ship new code.
- Ref = ID card, GetAtt = details; a queue's ID card is its URL.
- One bang at a time: never two short-form tags back to back.
- Parameters ask, mappings know, Parameter Store remembers.
NoEchois a mask, not a vault.- Exports are contracts;
GetStackOutputis a phone call. - Named IAM needs NAMED; macros need AUTO_EXPAND.
- No pass, no deploy:
iam:PassRoleon the service role. - Look before you leap: create the change set, then execute it.
- Freeze frame:
--disable-rollbackkeeps the evidence. - Continue the rollback, skip the casualties.
DeletionPolicyacts after the funeral;UpdateReplacePolicyafter the rebuild.- RDS keeps a photo: its default
DeletionPolicyisSnapshot. - Stack policy guards updates; termination protection guards deletes.
- Custom resources always call back, even to say they failed.
- New physical ID = new thing: the old one gets a
Delete. - Init configures, Signal reports, Hup listens.
- The exit code is the message:
cfn-signal -e $?. - Admin calls, execution acts: the two StackSets roles.
- Drift detection reports; it never repairs.
- Dynamic references are read once, not watched.