CloudFormation — DVA-C02 Mastery Guide
- Sources: course chapter 11 (CloudFormation). The chapter serves as the syllabus only; all wording here is original. The module scope also covers change sets, nested stacks, drift detection,
cfn-init/cfn-signal,UpdateReplacePolicyand dynamic references, which the chapter skips or only names.- Verified against AWS documentation on 2026-10-12. The marker
⚠️ Changed: <current>. Older material may say <old>.flags a value or behaviour that differs from the course or older practice tests: learn the current value.⚠️ unverifiedmarks a value that current AWS documentation doesn't confirm: don't rely on it. Every change is listed in Changed vs older material; confirmed rows also go tonotes/current-facts.md.- Tier 2 (deployment and messaging), bank target 35 questions. Domain: Deployment.
- How to use it:
/explain <topic>teaches from these sections (concept → exam facts → developer angle → traps → 3 check questions);/quiz cloudformationasks bank questions whosesource_notelinks point at the## N.headings below;/drill cloudformationfires short recall prompts fromnotes/cloudformation-cheat-sheet.md. Suggested loop: read a section and answer its practice questions before opening the answers →/explainwhat is still unclear →/quiz cloudformation→/drill cloudformation.
How CloudFormation appears on DVA-C02
DVA-C02 treats CloudFormation as the developer's deployment engine: you write or generate a template, ship it through a pipeline, and must predict what CloudFormation does when something goes wrong. Template trivia rarely appears on its own; it is wrapped in a deployment scenario.
- Authoring: which section or function solves a need (parameter vs mapping vs SSM parameter type,
RefvsFn::GetAtt,Fn::Sub, conditions) and how stacks share values (exports vs nested stacks). - Safe change: previewing with change sets, keeping data with
DeletionPolicyandUpdateReplacePolicy, blocking updates with stack policies, blocking deletes with termination protection. - Permissions: service roles plus
iam:PassRole, theCAPABILITY_*acknowledgements, least-privilege pipelines. - Troubleshooting: stack states (
ROLLBACK_COMPLETE,UPDATE_ROLLBACK_FAILED,DELETE_FAILED),InsufficientCapabilities, an export still in use, a missingcfn-signal, a custom resource that never answers. - Scale and extension: StackSets across accounts and Regions, custom resources, drift detection and resource import.
Typical question shapes: a failed deployment and its error to diagnose; "Which TWO actions…" around protection or permissions; "least operational overhead" choices between StackSets, nested stacks, exports and scripts; the exact CLI flag, attribute or pseudo parameter that does the job. Domain map: every topic maps to Deployment (prepare artifacts → §1, §5; automate and test deployments → §6–§8, §11; deploy across accounts → §12); security-flavoured (§6, §14) and troubleshooting-flavoured (§1, §8, §11) questions still count as Deployment in this bank.
Out of scope here: SAM syntax and the sam CLI (see notes/sam-guide.md), CDK (see notes/cdk-guide.md), the CodePipeline CloudFormation action and manual approvals (see notes/cicd-guide.md), Elastic Beanstalk's own stacks (see notes/beanstalk-guide.md), Parameter Store and Secrets Manager internals (see notes/security-encryption-guide.md), iam:PassRole mechanics (see notes/advanced-identity-guide.md), user data and Auto Scaling instance refresh (see notes/ec2-mastery-guide.md §3 and §10). Network design inside templates is out of the exam's scope.
| # | Topic | Why it is on the exam |
|---|---|---|
| 1 | Stacks and the deployment workflow | Template size limits, package/deploy, stack states and update behaviours drive most troubleshooting questions |
| 2 | Template sections and parameters | Which section does what; parameter types that validate input or fetch the latest AMI |
| 3 | Mappings conditions and outputs | Static lookups vs inputs, environment-specific resources, values other stacks consume |
| 4 | Intrinsic functions and pseudo parameters | Ref vs Fn::GetAtt return values, Fn::Sub, short-form YAML rules |
| 5 | Exports and nested stacks | Sharing values between stacks: strong exports, nested stacks for reuse, cross-account outputs |
| 6 | Service roles and capabilities | Least-privilege deployments, iam:PassRole, InsufficientCapabilities |
| 7 | Change sets | Preview replacements before they happen; validated and drift-aware deployments |
| 8 | Rollbacks and stack failure options | Recovering from ROLLBACK_COMPLETE, UPDATE_ROLLBACK_FAILED and DELETE_FAILED |
| 9 | Protecting resources and stacks | DeletionPolicy, UpdateReplacePolicy, stack policies and termination protection compared |
| 10 | Custom resources | Lambda- or SNS-backed logic for anything CloudFormation can't do natively |
| 11 | Helper scripts and creation policies | cfn-init, cfn-signal, cfn-hup, CreationPolicy and wait conditions |
| 12 | StackSets | One template across many accounts and Regions; permission models and operation preferences |
| 13 | Drift detection and resource import | Finding out-of-band changes and adopting existing resources |
| 14 | Dynamic references | Parameter Store and Secrets Manager values in templates without exposing them |
1. Stacks and the deployment workflow
Concept
- A template (JSON or YAML) declares the resources you want; a stack is one deployed copy of a template, named uniquely per Region and managed as a unit: create, update and delete apply to every resource in it.
- CloudFormation is declarative: it derives a dependency graph from
Ref,Fn::GetAtt,Fn::SubandDependsOn, creates independent resources in parallel and deletes in reverse order. - An update never edits the stored template in place: you submit a new template and/or new parameter values, CloudFormation compares them with the current stack and touches only what differs. Every property has a documented update behaviour: No interruption, Some interruption or Replacement.
- Deployment paths: the console (template studio), the CLI or SDK (
create-stack,update-stack,deploy), CI/CD pipelines, and generators such as SAM and CDK that emit templates. - Payoff: reviewable, version-controlled, repeatable environments that can be torn down when idle and rebuilt on demand, plus stack tags for cost tracking.
Exam facts
- Template size: an inline body (
--template-body,TemplateBody) can be up to 51,200 bytes; larger templates, up to 1 MB, must be passed as an S3https://URL (--template-url). ⚠️ Changed: templates up to 51,200 bytes are accepted inline; only larger ones must sit in S3 (the console uploads a local file to a CloudFormation-managed bucket for you). Older material may say every template must first be uploaded to S3. - Template quotas (per template; the fix is splitting, not a quota increase): 500 resources, 200 parameters, 200 outputs, 200 mappings with 200 attributes each, 60 dynamic references, description 1,024 bytes, parameter value 4,096 bytes, logical IDs and names 255 characters. ⚠️ Changed: since October 2020, 500 resources, 200 parameters, 200 outputs, 200 mappings and a 1 MB S3 template. Older material may say 200 resources, 60 parameters, 60 outputs, 100 mappings and 450 KB. Above 500 resources, split the template with nested stacks (§5).
- Account quota: 2,000 stacks per Region (adjustable);
REVIEW_IN_PROGRESSstacks count. Stack names: up to 128 characters, letters, digits and hyphens, starting with a letter. - Stack options: up to 50 stack tags (propagated to the resources), up to 5 SNS topics in
NotificationARNsfor stack events,--timeout-in-minutesto fail a creation that runs too long. - States:
CREATE_IN_PROGRESS→CREATE_COMPLETE; a failed create ends inROLLBACK_COMPLETE, where the only allowed operation is delete.UPDATE_COMPLETE_CLEANUP_IN_PROGRESSmeans the update succeeded and replaced resources are being removed (the stack is usable). A failed update ends inUPDATE_ROLLBACK_COMPLETE(usable, update again) orUPDATE_ROLLBACK_FAILED(§8). Deleted stacks stay listed for 90 days. - Replacement creates the new resource first, repoints references, then deletes the old one during cleanup, so it briefly needs quota for both. A resource with a custom name (
BucketName,TableName,FunctionName,QueueName) can't be replaced: change the name or drop it. - Lambda code in S3: a new object uploaded to the same key is not detected. Change
S3KeyorS3ObjectVersion;aws cloudformation packagedoes this by uploading under a checksum-based key. packageanddeploy:packageuploads local artifacts (Lambda code folders, nested templates, OpenAPI files) to S3 and writes a rewritten template;deploycreates and executes a change set for a new or existing stack. Useful flags:--no-execute-changeset(stop at the preview),--parameter-overrides Key=Value,--capabilities,--s3-bucket(needed when the template exceeds 51,200 bytes),--fail-on-empty-changeset(by default an empty change returns exit code 0).validate-templatechecks only JSON/YAML syntax and lists parameters, required capabilities and transforms; it doesn't check property values. Stack and change set operations add pre-deployment validation (§7).- ⚠️ Changed: the console's visual editor is template studio (template text beside a live diagram; validate, convert YAML↔JSON, save to S3; you edit the text, not the diagram). The standalone AWS Infrastructure Composer console ends support on 2026-12-07; its full experience stays in the AWS Toolkit for VS Code. Older material may say to design templates in Infrastructure Composer or CloudFormation Designer.
- ⚠️ Changed: express mode (
--deployment-config '{"mode": "EXPRESS"}'on create, update, delete, change sets androllback-stack) reports each resource complete once its configuration is applied, without waiting for stabilization. Rollback is off by default in this mode (add"disableRollback": falseto keep it), StackSets don't support it and custom resources still wait for their response: use it for development loops only. Older material may say every stack operation waits for each resource to stabilize. - The total number of resource types is ⚠️ unverified: the course quotes "over 700", AWS publishes no current figure and the list keeps growing. Look types up in the CloudFormation Template Reference.
- Pricing: no CloudFormation charge for resource providers in the
AWS::*andAlexa::*namespaces (you pay only for the resources); third-party registry resource types and Hooks are billed per handler operation (1,000 operations a month free).
Developer angle
aws cloudformation create-stack --stack-name orders-dev --template-body file://orders.yaml \
--parameters ParameterKey=Env,ParameterValue=dev --capabilities CAPABILITY_IAM --tags Key=team,Value=orders
aws cloudformation wait stack-create-complete --stack-name orders-dev
aws cloudformation describe-stack-events --stack-name orders-dev --output table \
--query "StackEvents[?contains(ResourceStatus, 'FAILED')].[LogicalResourceId,ResourceStatusReason]"
# new parameter value, same template
aws cloudformation update-stack --stack-name orders-dev --use-previous-template --capabilities CAPABILITY_IAM \
--parameters ParameterKey=Env,UsePreviousValue=true ParameterKey=Size,ParameterValue=large
# upload local artifacts, then create or update through a change set in one command
aws cloudformation package --template-file orders.yaml --s3-bucket orders-artifacts-111122223333 \
--output-template-file packaged.yaml
aws cloudformation deploy --template-file packaged.yaml --stack-name orders-dev \
--parameter-overrides Env=dev --capabilities CAPABILITY_IAM
- SDK equivalents (boto3):
create_stack(StackName=…, TemplateBody=…, Parameters=[…], Capabilities=["CAPABILITY_IAM"]),get_waiter("stack_create_complete").wait(StackName=…),describe_stacks(StackName=…)["Stacks"][0]["Outputs"]. - Errors to recognise: a duplicate stack name returns
AlreadyExists(AlreadyExistsExceptionin the CLI and SDKs); a quota returnsLimitExceeded; anupdate-stackwith identical template and parameters returns aValidationErrorsaying there are no updates to perform, whiledeployjust reports no changes. - In
describe-stack-events, the earliest*_FAILEDevent'sResourceStatusReasonusually names the root cause; later failures are mostly knock-on cancellations.
Traps
- Trap: "Every template has to be uploaded to S3 before
create-stack." → Reality: bodies up to 51,200 bytes go inline; S3 (up to 1 MB) is needed only above that. - Trap: "Overwrite
orders.zipin S3 and update the stack." → Reality: an unchangedS3Key/S3ObjectVersionis no change; use a new key or version (packagedoes it). - Trap: "
validate-templatepassed, so the stack will deploy." → Reality: it checks syntax only; property errors surface in pre-deployment validation or during provisioning. - Trap: "Fix a
ROLLBACK_COMPLETEstack withupdate-stack." → Reality: delete it and create it again (or create with failure preservation next time, §8). - Trap: "Change a replacement-only property of a table that has
TableNameset." → Reality: the update fails; give the table a new name or remove the custom name. - Trap: "
UPDATE_COMPLETE_CLEANUP_IN_PROGRESSmeans the update is failing." → Reality: the update succeeded; old physical resources are being deleted. - Trap: "Use express mode in production to finish deployments faster." → Reality: it skips stabilization and disables rollback by default; production keeps the standard mode.
Practice questions
-
A build job uploads each new version of a Lambda function to
s3://orders-artifacts/builds/orders.zipand then runsaws cloudformation update-stackwith the unchanged template, whoseAWS::Lambda::Functionresource pointsS3BucketandS3Keyat that object. The job reports that there are no updates to perform, and the function keeps running the previous code. Which change fixes the pipeline with the LEAST effort?- A. Call
update-stackwith--use-previous-templateinstead of uploading the template again. - B. Run
aws cloudformation packagein the job to upload each build under a new checksum-based S3 key. - C. Run
aws cloudformation detect-stack-drifton the stack before eachupdate-stackcall in the job. - D. Add a
DependsOnattribute from the function to the artifact bucket resource in the template'sResourcessection.
- A. Call
-
A developer's first
create-stackfor a new service failed on a misconfigured resource, and the stack is now inROLLBACK_COMPLETE. The template has been corrected. The service must be deployed with the corrected template, and the next failed creation must keep the successfully created resources for troubleshooting. Which TWO actions meet these requirements? (Select TWO.)- A. Run
update-stackon the existing stack with the corrected template. - B. Delete the stack and run
create-stackagain with the corrected template. - C. Run
continue-update-rollbackon the stack, then update it. - D. Add
--disable-rollback(Preserve successfully provisioned resources) to futurecreate-stackcalls. - E. Pass
--enable-termination-protectionon the nextcreate-stackcall.
- A. Run
Answers
- B.
packageuploads the artifact under a name derived from its checksum and rewritesS3Key, so CloudFormation sees a changed property and deploys the new code. A submits the same template again (still no change); C only reports drift, never redeploys, and doesn't compare Lambda code; D changes ordering, not change detection. - B, D. A stack in
ROLLBACK_COMPLETEcan only be deleted, so it must be recreated;--disable-rollbackkeeps successful resources and leaves failed ones for inspection. A is rejected in that state; C applies only toUPDATE_ROLLBACK_FAILED; E protects against stack deletion, not against rollback.
2. Template sections and parameters
Concept
- A template is a set of top-level sections; only
Resourcesis required:
| Section | Purpose |
|---|---|
AWSTemplateFormatVersion |
Optional; the only valid value is 2010-09-09, a literal (latest assumed when omitted) |
Description |
Literal string, up to 1,024 bytes |
Metadata |
Extra information, e.g. AWS::CloudFormation::Interface (parameter groups and order in the console) |
Parameters |
Inputs supplied at create and update time |
Rules |
Validates parameter values and combinations before any resource changes |
Mappings |
Static two-level lookup tables (§3) |
Conditions |
Booleans that decide which resources, outputs and property values exist (§3) |
Transform |
Macros applied before deployment: AWS::Serverless-2016-10-31 (SAM), AWS::Include, AWS::LanguageExtensions |
Resources |
Required: each resource has a logical ID, a Type of the form Provider::Service::Resource (AWS::EC2::Instance) and Properties |
Outputs |
Values to display, export or reference (§3, §5) |
- Parameters make one template reusable across environments and accounts. Rule of thumb: a value that differs per deployment and can't be derived is a parameter; a value derivable from the Region, account or environment is a mapping or a condition; a value managed elsewhere (latest AMI, shared settings) comes from Parameter Store.
Exam facts
Typeis the only required attribute. Base types:String,Number(validated as a number, butRefreturns a string),List<Number>,CommaDelimitedList(items are space-trimmed;Refreturns a list). Parameters are local to their template: other stacks can'tRefthem (share values through outputs, §5).- Constraints:
AllowedValues,AllowedPattern(regex matching the whole value, or each item of a list),MinLength/MaxLength(String),MinValue/MaxValue(Number),ConstraintDescription(custom error text),Default,Description(up to 4,000 characters),NoEcho. YAML quirk: quote'Yes'/'No'inAllowedValues, or the parser reads booleans. - No
Defaultand no value supplied → the operation fails withParameters: [Name] must have values. - AWS-specific types validate against existing resources in the deploying account and Region and give console drop-downs:
AWS::EC2::KeyPair::KeyName,AWS::EC2::VPC::Id,AWS::EC2::Subnet::Id,AWS::EC2::SecurityGroup::Id,AWS::EC2::Image::Id,AWS::EC2::AvailabilityZone::Name,AWS::Route53::HostedZone::Id, plusList<…>variants. Values from another account → useString. - SSM parameter types take a Parameter Store name and resolve its value at every create, update and change set:
AWS::SSM::Parameter::Value<String>,AWS::SSM::Parameter::Value<List<String>>,AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>(latest AMI from a public parameter),AWS::SSM::Parameter::Name(existence check only).UsePreviousValuekeeps the parameter key, not the old value. Constraints are checked against the key, not the value. A parameter shared from another account needs its full ARN.SecureStringis not supported as a parameter type: use anssm-securedynamic reference (§14). NoEcho: trueshows*****in describe calls and events but doesn't mask a value you copy intoOutputs, theMetadatasection or a resource'sMetadataattribute; AWS recommends dynamic references for secrets.- Rules (optional
RuleConditionplus requiredAssertionsofAssert/AssertDescription) use rule functions (Fn::Contains,Fn::EachMemberEquals,Fn::EachMemberIn,Fn::RefAll,Fn::ValueOf,Fn::ValueOfAll, plusFn::And,Fn::Or,Fn::Not,Fn::Equals,Fn::If); a failed assertion stops the operation before any resource is created or updated. - Transform: a template with transforms is deployed through a change set (what
deploydoes) or directly withCAPABILITY_AUTO_EXPAND(§6). SAM resources andGlobals: seenotes/sam-guide.md.
Developer angle
Parameters with validation, an SSM parameter type and a rule:
Parameters:
Env: {Type: String, AllowedValues: [dev, prod], Default: dev}
InstanceType: {Type: String, Default: t3.micro, AllowedPattern: "^[a-z0-9]+\\.[a-z0-9]+$",
ConstraintDescription: "must look like family.size, for example t3.micro"}
VpcId: {Type: "AWS::EC2::VPC::Id"}
SubnetIds: {Type: "List<AWS::EC2::Subnet::Id>"}
LatestAmiId:
Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
Default: /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64
Rules:
ProdInstanceSize:
RuleCondition: !Equals [!Ref Env, prod]
Assertions:
# x86_64 types, like the AMI (Graviton types such as m7g need the -arm64 AMI parameter)
- Assert: {"Fn::Contains": [[m7i.large, m7i.xlarge], !Ref InstanceType]}
AssertDescription: prod stacks must use m7i.large or m7i.xlarge
Resources:
WorkerSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: !Sub "Orders workers (${Env})"
VpcId: !Ref VpcId
# list-type values: escape the commas
aws cloudformation create-stack --stack-name orders-workers --template-body file://workers.yaml \
--parameters ParameterKey=VpcId,ParameterValue=vpc-0a1b2c3d4e5f67890 'ParameterKey=SubnetIds,ParameterValue=subnet-0a1b2c3d\,subnet-4e5f6a7b'
aws cloudformation get-template-summary --template-body file://workers.yaml --query Parameters
- Console drop-downs for AWS-specific types need describe permissions (
ec2:DescribeVpcs,ec2:DescribeSubnets,ec2:DescribeKeyPairs,ec2:DescribeSecurityGroups); a missing Parameter Store name in an SSM parameter type fails the operation with a validation error before anything is created.
Traps
- Trap: "
NoEchohides the password everywhere." → Reality: it is visible again wherever you copy it (outputs, metadata); keep secrets out of parameters with dynamic references. - Trap: "
UsePreviousValueon an AMI parameter of an SSM type keeps last month's AMI." → Reality: it keeps the key and re-reads the latest value, which can replace instances on the next update. - Trap: "Declare the database password as
AWS::SSM::Parameter::Value<SecureString>." → Reality: not supported; use{{resolve:ssm-secure:…}}in a supported property. - Trap: "Use
AWS::EC2::Subnet::Idso users can pick a subnet from another account." → Reality: AWS-specific types only accept values from the deploying account and Region; useString. - Trap: "Conditions reject invalid input." → Reality: conditions only include or skip resources;
AllowedValues,AllowedPatternandRulesreject input.
Practice questions
-
A developer maintains a template that launches EC2 instances in several Regions. Every deployment must use the latest Amazon Linux 2023 AMI for its Region, without anyone editing the template or looking up AMI IDs. Which approach meets these requirements with the LEAST operational overhead?
- A. A
Mappingssection keyed by Region that lists the current Amazon Linux 2023 AMI ID for every Region the company uses. - B. A
Stringparameter with anAllowedPatternof^ami-[0-9a-f]{17}$and aConstraintDescriptionthat explains the format. - C. An
AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>parameter that defaults to the public AL2023 parameter. - D. A parameter of type
AWS::EC2::Image::Idwhose default value is the AMI ID that is current at the time of each release.
- A. A
-
A template takes
Env(devorprod) andInstanceType. CloudFormation must reject acreate-stackrequest before any resource is created whenEnvis neither value, or whenEnvisprodandInstanceTypeisn'tm7g.large. Which TWO template features meet these requirements? (Select TWO.)- A.
AllowedValueson theEnvparameter. - B. A condition
IsProdattached to the instance resource. - C.
NoEchoon theInstanceTypeparameter. - D. A
Rulesentry whoseRuleConditionchecksEnvand whose assertion usesFn::ContainsonInstanceType. - E. An
AWS::CloudFormation::WaitConditionthat the instance depends on.
- A.
Answers
- C. An SSM parameter type resolves the public parameter at every create and update, so each Region gets its latest AMI without edits. A and D need someone to maintain IDs; B only validates the shape of an ID that someone still has to look up.
- A, D.
AllowedValuesrejects any otherEnv; a rule validates the combination of two parameters before provisioning. B only skips or creates the instance; C masks a value; E waits for signals after resources already exist.
3. Mappings conditions and outputs
Concept
- Mappings are fixed lookup tables inside the template (Region → instance size, environment → log retention). Pick a mapping when the set of possible values is closed and the right entry follows from a key CloudFormation can supply: the Region, the account, an environment parameter.
- Conditions decide at create and update time whether a resource or output exists and which property value applies; typical drivers are the environment, the Region or an optional parameter left empty.
- Outputs publish values after the stack is built: shown in the console and
describe-stacks, read by scripts and pipelines, exported to other stacks (§5).
Exam facts
- Mappings have two levels (map → top-level key → second-level key → value); keys are literal strings; values are strings or lists; the section can't contain parameters, pseudo parameters or functions. Read them with
!FindInMap [MapName, TopLevelKey, SecondLevelKey], whose keys can use onlyRefand nestedFn::FindInMap(more functions withAWS::LanguageExtensions). Mapping = known, static, keyed choices; parameter = user-specific input; Parameter Store = values that change outside the template (AWS recommends it over Region → AMI maps). - ⚠️ Changed: an optional fourth argument
DefaultValuereturns a fallback instead of failing on a missing key, in any template. Older material may sayDefaultValuerequires theAWS::LanguageExtensionstransform. - Condition functions:
Fn::Equals(two values),Fn::AndandFn::Or(2–10 conditions each),Fn::Not(one condition),Fn::If([condition, value_if_true, value_if_false]) andCondition: Nameto reuse a condition. Inputs: parameters, pseudo parameters, mappings and other conditions, never resource attributes. - Apply a condition with the
Conditionattribute on a resource or output; useFn::Ifin resource properties, outputs, theMetadataattribute andUpdatePolicy. Returning!Ref AWS::NoValuefromFn::Ifremoves the property entirely. - Conditions are re-evaluated on every update: a resource whose condition becomes false is deleted (its
DeletionPolicyapplies, §9). Anything that references a conditional resource needs the same condition or anFn::If, otherwise the template fails when the condition is false. - CloudFormation doesn't count edits that only touch conditions, outputs,
DeletionPolicyorUpdatePolicy(or unused mapping values) as an update; include a resource change, for example in a resource'sMetadataattribute. - Outputs: up to 200 per template;
Valuerequired;Descriptiona literal of up to 1,024 bytes; optionalExport(§5) andCondition. Output values aren't redacted (never output secrets) and aren't available while the stack is in an*_IN_PROGRESSstate.
Developer angle
Mapping with a fallback, conditions, a conditional resource, a removable property and outputs:
Parameters:
Env: {Type: String, AllowedValues: [dev, prod]}
AlarmEmail: {Type: String, Default: ""}
Mappings:
EnvSettings:
dev: {LogRetention: "7"}
prod: {LogRetention: "365"}
Conditions:
IsProd: !Equals [!Ref Env, prod]
ProdWithEmail: !And [!Condition IsProd, !Not [!Equals [!Ref AlarmEmail, ""]]]
Resources:
AppLogs:
Type: AWS::Logs::LogGroup
Properties:
RetentionInDays: !FindInMap [EnvSettings, !Ref Env, LogRetention, {DefaultValue: "30"}]
ReportsBucket:
Type: AWS::S3::Bucket
Properties:
VersioningConfiguration: !If [IsProd, {Status: Enabled}, !Ref "AWS::NoValue"]
AlarmTopic:
Type: AWS::SNS::Topic
Condition: ProdWithEmail
Properties:
Subscription: [{Protocol: email, Endpoint: !Ref AlarmEmail}]
Outputs:
BucketName:
Value: !Ref ReportsBucket
AlarmTopicArn:
Condition: ProdWithEmail
Value: !Ref AlarmTopic
aws cloudformation describe-stacks --stack-name orders-prod \
--query "Stacks[0].Outputs[?OutputKey=='BucketName'].OutputValue" --output text
Traps
- Trap: "Base a condition on whether a resource was created or on its attribute." → Reality: conditions see only parameters, pseudo parameters, mappings and other conditions.
- Trap: "Put
!Ref Envinside a mapping value." → Reality: theMappingssection is static; select keys with!RefinsideFn::FindInMapinstead. - Trap: "An output can reference a conditional resource on its own." → Reality: give the output the same
Condition(or anFn::If), or the stack fails when the condition is false. - Trap: "Change only an output and run
update-stack." → Reality: not treated as an update; pair it with a resource change. - Trap: "Switching
Envfromprodtodevjust hides the prod-only database." → Reality: the resource is deleted on that update unless itsDeletionPolicykeeps it.
Practice questions
-
A developer uses one template for
devandprodstacks. Onlyprodstacks must contain an Amazon SQS dead-letter queue;devstacks must not create it. Which approach meets this requirement with the LEAST template duplication?- A. Add a condition based on the
Envparameter and set it as the queue'sConditionattribute. - B. Maintain separate
dev.yamlandprod.yamltemplates and have the pipeline select the right one for each environment. - C. Put the queue's properties in a
Mappingsentry forprodand leave thedeventry empty. - D. Set
DeletionPolicy: Deleteon the queue and delete it by hand after eachdevdeployment.
- A. Add a condition based on the
-
In a shared template, an
AWS::RDS::DBInstancenamedReportsDbis created only when theIsProdcondition is true. An outputReportsEndpointreturns!GetAtt ReportsDb.Endpoint.Address. Creating adevstack from the template fails. Which TWO changes would each let the same template deploy in both environments? (Select TWO.)- A. Add
DependsOn: ReportsDbto theReportsEndpointoutput. - B. Move the
ReportsDbdefinition into aMappingsentry forprod. - C. Add
Condition: IsProdto theReportsEndpointoutput. - D. Replace
Fn::GetAttwithFn::ImportValuein the output. - E. Change the output value to
!If [IsProd, !GetAtt ReportsDb.Endpoint.Address, "not-deployed"].
- A. Add
Answers
- A. A condition attached to the resource creates it only when
IsProdis true, in a single template. B duplicates the template; C is impossible because mappings hold values, not resources; D creates the queue anyway and adds manual work. - C, E. Either give the output the same condition or make its value conditional with
Fn::If. A isn't a valid output attribute and wouldn't help; B is impossible (mappings can't hold resources); D reads another stack's export, unrelated to the missing resource.
4. Intrinsic functions and pseudo parameters
Concept
- Intrinsic functions compute values during stack operations. They are allowed in resource properties, outputs, the
Metadataattribute,UpdatePolicyand conditions; not inParameters, mapping keys orAWSTemplateFormatVersion. - Pseudo parameters are always available without declaration:
AWS::AccountId,AWS::Region,AWS::StackName,AWS::StackId(stack ARN),AWS::Partition(aws,aws-cn,aws-us-gov),AWS::URLSuffix(amazonaws.com),AWS::NotificationARNsandAWS::NoValue(removes a property). - References through
Ref,Fn::GetAttandFn::Subalso create the dependencies that order creation;DependsOnis only for hidden dependencies (for example an Elastic IP that needs the internet gateway attachment, or a resource that must wait for a separate IAM policy).
Exam facts
| Function | Returns | Short-form example |
|---|---|---|
Ref |
Parameter value; for a resource, its identifier (type-specific) | !Ref OrdersTable |
Fn::GetAtt |
A named attribute of a resource | !GetAtt OrdersTable.Arn |
Fn::Sub |
String with ${Param}, ${Resource}, ${Resource.Attr}, ${AWS::Region} substituted |
!Sub "arn:${AWS::Partition}:s3:::${Bucket}/*" |
Fn::Join |
Items joined with a delimiter | !Join [",", [a, b]] |
Fn::Select, Fn::Split |
Item by index; list from a string | !Select [0, !GetAZs ""] |
Fn::FindInMap |
Mapping value (§3) | !FindInMap [Map, Key, Name] |
Fn::ImportValue, Fn::GetStackOutput |
Another stack's export or output (§5) | Fn::ImportValue: !Sub "${Net}-VpcId" |
Fn::Base64 |
Base64 of a string, for EC2 UserData |
Fn::Base64: !Sub | |
Fn::GetAZs |
Availability Zones of a Region, alphabetical (only those with a default subnet when any exist); "" = current Region |
!GetAZs "" |
Fn::Cidr, Fn::Transform |
CIDR blocks; run a macro on a snippet | |
Fn::ForEach, Fn::Length, Fn::ToJsonString |
Loops, list length, JSON string; require Transform: AWS::LanguageExtensions |
full form only |
- What
Refreturns for common resources:AWS::EC2::Instance→ instance ID;AWS::S3::Bucket→ bucket name;AWS::SQS::Queue→ queue URL (ARN via!GetAtt Queue.Arn);AWS::SNS::Topic→ topic ARN;AWS::Lambda::Function→ function name (ARN via!GetAtt Fn.Arn);AWS::IAM::Role→ role name (!GetAtt Role.Arn);AWS::DynamoDB::Table→ table name (!GetAtt Table.Arn,!GetAtt Table.StreamArn);AWS::EC2::EIP→ IP address. The resource's Template Reference page listsRefandGetAttvalues. - ⚠️ Changed: the function is named
Ref(JSON{"Ref": "Name"}, YAMLRef: Nameor!Ref Name); there is noFn::prefix. Older material may sayFn::Ref. Fn::Subneeds no variable map for parameters, resources, attributes and pseudo parameters;${!Literal}writes a literal${Literal}(inside launch template data write\${!Literal}, or it resolves to an empty string); the template string itself can't contain functions (use the variable map).- Without the language extensions, the
Fn::GetAttlogical name must be a literal and the attribute name can use onlyRef. - Short-form YAML rule: a short-form function can't directly wrap another short-form function:
!Base64 !Sub …,!GetAZs !Ref …and!ImportValue !Sub …are invalid. Write the outer one in full form (Fn::Base64: !Sub |). - Language extensions:
Fn::ForEachreplicates resources, outputs or conditions over a list (quotas apply to the expanded template); use full names (Fn::Length, not!Length); with SAM, listAWS::LanguageExtensionsbeforeAWS::Serverless-2016-10-31; avoid--use-previous-templatebecause the stored processed template holds already-resolved literals.
Developer angle
Parameters:
LatestAmiId:
Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
Default: /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64
Resources:
ReportsBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub "reports-${AWS::AccountId}-${AWS::Region}"
WorkerInstance:
Type: AWS::EC2::Instance
Properties:
ImageId: !Ref LatestAmiId
InstanceType: t3.micro
AvailabilityZone: !Select [0, !GetAZs ""]
UserData:
Fn::Base64: !Sub |
#!/bin/bash
echo "STACK=${AWS::StackName} BUCKET=${ReportsBucket}" > /etc/worker.env
echo 'Literal kept for the shell: ${!HOME}' >> /etc/worker.env
Outputs:
DailyReportsArn:
Value: !Sub "${ReportsBucket.Arn}/daily/*"
# physical ID behind a logical ID (what Ref returned)
aws cloudformation describe-stack-resource --stack-name reports --logical-resource-id ReportsBucket \
--query StackResourceDetail.PhysicalResourceId --output text
Traps
- Trap: "
!Ref MyQueuegives the ARN for an IAM policy." → Reality: it gives the queue URL; use!GetAtt MyQueue.Arn. - Trap: "
!Ref MyFunctionis the function ARN." → Reality: it is the name;!GetAtt MyFunction.Arnfor ARNs (permissions, event sources). - Trap: "
UserData: !Base64 !Sub |is fine." → Reality: two consecutive short forms are invalid; useFn::Base64: !Sub |. - Trap: "Put
!GetAttinside theFn::Substring." → Reality: write${Resource.Attribute}in the string, or pass functions through the variable map. - Trap: "Use
Fn::ForEachin any template." → Reality: it needsTransform: AWS::LanguageExtensions. - Trap: "Write ARNs as
arn:aws:…with a literal account ID and Region." → Reality: use${AWS::Partition},${AWS::AccountId}and${AWS::Region}so the template works in every account, Region and partition (China, GovCloud). - Trap: "In JSON, reference a parameter with
{"Fn::Ref": "Env"}." → Reality: the function isRef:{"Ref": "Env"}.
Practice questions
-
A template creates an
AWS::SQS::Queuewith the logical IDJobsQueueand an inline IAM policy that must allowsqs:SendMessageon that queue. WithResource: !Ref JobsQueue, the stack fails because IAM rejects the policy document. Which value should theResourceelement use?- A.
!Sub "arn:aws:sqs:::${JobsQueue}" - B.
!Ref JobsQueue.Arn - C.
!GetAtt JobsQueue.QueueUrl - D.
!GetAtt JobsQueue.Arn
- A.
-
A developer writes
UserData: !Base64 !Sub |followed by a script that uses${AWS::StackName}and${AWS::Region}, and CloudFormation rejects the template. The script must keep its substitutions. Which TWO rewrites are valid? (Select TWO.)- A.
UserData: !Base64 !Joinfollowed by a list that rebuilds the script line by line. - B.
UserData:followed by an indentedFn::Base64: !Sub |block. - C.
UserData: !Sub !Base64 |followed by the script. - D.
UserData: !Sub |followed by the script, because CloudFormation base64-encodesUserDataautomatically. - E.
UserData: !Base64followed by an indented"Fn::Sub": |block.
- A.
Answers
- D.
Refon a queue returns its URL; the policy needs the ARN, whichFn::GetAttprovides. A builds an invalid ARN without Region and account; B isn't valid syntax forRef; C returns the URL again. - B, E. Either the outer or the inner function must use its full name. A and C still chain two short forms; D fails because
AWS::EC2::Instanceexpects base64 text inUserData.
5. Exports and nested stacks
Concept
- Cross-stack references (exports): a producer stack exports an output (
Export: Name); consumers read it withFn::ImportValue. Stacks keep separate lifecycles and owners (the platform team's network stack, each app team's stack), and CloudFormation enforces the link. - Nested stacks: a parent declares
AWS::CloudFormation::Stackresources pointing to child templates in S3; the root and all children deploy, update and delete as one unit. Use them for reusable components (a standard load balancer, a hardened bucket) and to get past the 500-resource limit. - ⚠️ Changed:
Fn::GetStackOutputreads an output of any stack — same account, another Region or another account (throughRoleArn) — without an export, as a weak reference resolved at deploy time. Older material may say stack outputs can be shared only within one account and Region.
Exam facts
- Export names are unique per account per Region;
Fn::ImportValueworks only within the same account and Region. - While any stack imports an export, CloudFormation refuses to delete the exporting stack or to change or remove that output.
list-imports --export-nameshows the consumers; remove their imports first. - An export
Nameand anImportValueargument may use functions (typically!Sub "${NetworkStackName}-VpcId") but notReforGetAtton resources. !ImportValuecan't wrap the!Subshort form: writeFn::ImportValue: !Sub "…".- Nested stacks:
TemplateURL(S3;aws cloudformation packageuploads local child templates and rewrites it) and stringParametersfor the child; the parent reads child outputs with!GetAtt ChildStack.Outputs.OutputName. Updates, change sets and deletes start from the root stack (only changed children are updated); updating a nested stack directly is discouraged. Termination protection is set on the root and inherited. One nested-stack operation can touch at most 2,500 resources. - Choosing: share values with other, independently managed stacks → exports; reuse a component inside one deployment unit → nested stacks; a value from another account or Region →
Fn::GetStackOutput. Fn::GetStackOutputtakesStackName,OutputName, optionalRegionand optionalRoleArn(a role in the producer account withcloudformation:DescribeStacks, assumable by the consumer stack's execution role). It gives no referential integrity (protect the producer yourself), consumers see a new value only on their next update, cross-partition references aren't supported, and it currently works as a direct property value or insideFn::Join,Fn::IfandFn::Select(not as anOutputsvalue or inside anFn::Submap).
Developer angle
Producer exports; the consumer imports, nests a component and reads a cross-account output:
# network.yaml (producer)
Resources:
Vpc:
Type: AWS::EC2::VPC
Properties: {CidrBlock: 10.0.0.0/16}
Outputs:
VpcId:
Value: !Ref Vpc
Export:
Name: !Sub "${AWS::StackName}-VpcId"
# app.yaml (consumer)
Parameters:
NetworkStackName: {Type: String, Default: network-prod}
Resources:
AppSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: App tier
VpcId:
Fn::ImportValue: !Sub "${NetworkStackName}-VpcId"
ArtifactBucketStack:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: ./templates/hardened-bucket.yaml # package rewrites this to an S3 URL
Parameters: {RetentionDays: "30"}
ReportsQueue:
Type: AWS::SQS::Queue
Properties:
KmsMasterKeyId:
Fn::GetStackOutput:
StackName: shared-keys
OutputName: QueueKeyArn
Region: eu-west-1
RoleArn: arn:aws:iam::111111111111:role/ReadSharedOutputs
Outputs:
ArtifactBucketName:
Value: !GetAtt ArtifactBucketStack.Outputs.BucketName
aws cloudformation list-exports --query "Exports[].[Name,Value]" --output table
aws cloudformation list-imports --export-name network-prod-VpcId
Traps
- Trap: "Delete the network stack first; the app stacks keep working." → Reality: deletion is refused while any stack imports its exports.
- Trap: "Change an exported value in place." → Reality: refused while imported; add a new export, move consumers, then remove the old one.
- Trap: "
Fn::ImportValueacross Regions or accounts." → Reality: same account and Region only; useFn::GetStackOutput(weak) or pass values another way. - Trap: "Update a child nested stack directly." → Reality: update the root; it updates the children that changed.
- Trap: "Nest every app stack inside the network stack to share the VPC." → Reality: independent teams and lifecycles call for exports.
- Trap: "
Fn::GetStackOutputprotects the producer like an export." → Reality: it is a weak reference; a deleted producer breaks the consumer's next update.
Practice questions
-
A platform team owns a VPC stack. Twelve application teams deploy their own stacks on their own schedules and need the VPC ID and private subnet IDs. The platform team must be prevented from accidentally deleting or changing those values while any application uses them. Which approach meets these requirements?
- A. Nest each application stack inside the VPC stack as an
AWS::CloudFormation::Stackresource. - B. Export the IDs as VPC stack outputs and use
Fn::ImportValuein each application template. - C. Copy the IDs into each application template as
Stringparameter defaults and update them when the network changes. - D. Read the IDs in each application template with
Fn::GetStackOutput, which needs no export declaration.
- A. Nest each application stack inside the VPC stack as an
-
A developer wants to delete an old shared-network stack, but CloudFormation blocks the deletion because another stack imports one of its exports. The network stack must be deleted without leaving the consuming stacks broken. Which TWO actions should the developer take? (Select TWO.)
- A. Run
aws cloudformation list-imports --export-namewith the export's name to find the importing stacks. - B. Run
delete-stackwith--retain-resources, listing the logical IDs of the outputs that other stacks import. - C. Update each importing stack to stop using that export, then delete the network stack.
- D. Disable termination protection on the network stack.
- E. Rename the export in the network stack's
Outputssection and update the network stack.
- A. Run
Answers
- B. Exports with
Fn::ImportValuegive independent stacks a strong reference: the producer can't delete or change an export in use. A merges independent lifecycles into one unit; C copies values that silently go stale; D creates only a weak reference that doesn't block deletion. - A, C. Find the consumers, migrate them off the export, then delete. B applies to resources in
DELETE_FAILED, not to outputs; D isn't the blocker; E changes an exported value in use, which CloudFormation refuses.
6. Service roles and capabilities
Concept
- Without a service role, CloudFormation acts with a temporary session made from the caller's credentials, so the caller needs permissions for every resource in the template plus the CloudFormation actions.
- A service role is an IAM role that
cloudformation.amazonaws.comassumes for stack operations. Developers then need only CloudFormation permissions andiam:PassRoleon that role: they can deploy approved infrastructure without holding the underlying permissions (least privilege). - Capabilities are explicit acknowledgements for templates that can change permissions or rewrite themselves:
CAPABILITY_IAM,CAPABILITY_NAMED_IAM,CAPABILITY_AUTO_EXPAND. A missing one stops the operation before anything is created.
Exam facts
- Pass the role with
--role-arn(create-stack,update-stack,delete-stack,create-change-set,deploy). Once associated, CloudFormation uses it for all later operations on the stack, including for users who couldn't pass it themselves; it can be replaced on an update but not removed. A powerful service role is therefore a privilege-escalation path: keep it least privilege. - If no role is given, CloudFormation reuses the role already associated with the stack, otherwise the caller's session. That reuse doesn't satisfy a
cloudformation:RoleARNcondition: the key is set only when the request passes--role-arn, so under the developer policy below, anupdate-stackorcreate-change-setwithout it is implicitly denied. A deleted or under-privileged role makes operations fail with access-denied status reasons. - Long operations (RDS, Auto Scaling groups, nested stacks) can outlast the caller's session; AWS's fix for such stabilization timeouts is to deploy with a service role.
iam:PassRoleon the service role ARN is required to associate it; narrow it withiam:PassedToService=cloudformation.amazonaws.com. PassRole mechanics: seenotes/advanced-identity-guide.md.- CloudFormation condition keys (evaluated on create and update):
cloudformation:RoleARN(force a specific service role),cloudformation:TemplateUrl(only approved template URLs),cloudformation:StackPolicyUrl,cloudformation:ResourceTypes(allowed types; callers must send--resource-types),cloudformation:ImportResourceTypes,cloudformation:ChangeSetName. Don't useaws:SourceIp: CloudFormation calls other services from its own addresses. CAPABILITY_IAM: the template creates or changes IAM resources (AWS::IAM::Role,User,Group,Policy,ManagedPolicy,InstanceProfile,AccessKey,UserToGroupAddition) with generated names.CAPABILITY_NAMED_IAM: required as soon as one of them has a custom name (RoleName,UserName,GroupName,ManagedPolicyName…); it also covers the unnamed case.CAPABILITY_AUTO_EXPAND: needed to create or update a stack directly (without reviewing a change set) from a template that contains macros or transforms (AWS::Serverless,AWS::Include, custom macros), including templates that combine macros and nested stacks; oncreate-change-setit has no effect. ⚠️ Changed: nested stacks on their own don't need it; it is about macros. Older material may say any template with nested stacks needsCAPABILITY_AUTO_EXPAND.- Capabilities are acknowledged per request: every
update-stack,create-change-setordeployof a template that still contains IAM resources must send the IAM capability again (and a directupdate-stackof a template with macros needsCAPABILITY_AUTO_EXPANDagain). - A missing capability returns the API error
InsufficientCapabilities(HTTP 400; the CLI printsInsufficientCapabilitiesExceptionand names the capability).validate-templatereturns the requiredCapabilitiesand aCapabilitiesReason. Console equivalent: the "I acknowledge that this template may create IAM resources" check box. - Custom-named IAM resources are global per account: the same template with a fixed
RoleNamein two Regions collides. Add${AWS::Region}to the name or let CloudFormation generate it.
Developer angle
Trust policy of the service role, then a developer policy that only allows stacks deployed through that role:
{"Version": "2012-10-17",
"Statement": [{"Effect": "Allow",
"Principal": {"Service": "cloudformation.amazonaws.com"},
"Action": "sts:AssumeRole"}]}
{
"Version": "2012-10-17",
"Statement": [
{"Effect": "Allow",
"Action": ["cloudformation:CreateStack", "cloudformation:UpdateStack", "cloudformation:CreateChangeSet"],
"Resource": "arn:aws:cloudformation:eu-west-1:111122223333:stack/orders-*/*",
"Condition": {"StringEquals": {"cloudformation:RoleARN": "arn:aws:iam::111122223333:role/cfn-orders-deployer"}}},
{"Effect": "Allow",
"Action": ["cloudformation:ExecuteChangeSet", "cloudformation:DescribeStacks", "cloudformation:DescribeStackEvents"],
"Resource": "arn:aws:cloudformation:eu-west-1:111122223333:stack/orders-*/*"},
{"Effect": "Allow",
"Action": "iam:PassRole",
"Resource": "arn:aws:iam::111122223333:role/cfn-orders-deployer",
"Condition": {"StringEquals": {"iam:PassedToService": "cloudformation.amazonaws.com"}}}
]
}
aws cloudformation validate-template --template-body file://orders.yaml --query "[Capabilities,CapabilitiesReason]"
aws cloudformation create-stack --stack-name orders-prod \
--template-url https://orders-templates-111122223333.s3.eu-west-1.amazonaws.com/orders.yaml \
--role-arn arn:aws:iam::111122223333:role/cfn-orders-deployer --capabilities CAPABILITY_NAMED_IAM
Traps
- Trap: "Give developers the S3, Lambda and IAM permissions the template needs." → Reality: give the permissions to a service role; developers get CloudFormation actions plus
iam:PassRole. - Trap: "
CAPABILITY_IAMis enough for a role withRoleName." → Reality: custom names needCAPABILITY_NAMED_IAM. - Trap: "Every nested-stack template needs
CAPABILITY_AUTO_EXPAND." → Reality: macros and transforms do, when you skip the change set. - Trap: "Remove the service role from the stack later." → Reality: it can be replaced, not removed.
- Trap: "Only users who can pass the role can use it after creation." → Reality: anyone allowed to operate on the stack uses the associated role.
- Trap: "Restrict deployments with
aws:SourceIp." → Reality: CloudFormation calls services from its own IP addresses.
Practice questions
-
A company wants developers to deploy approved templates that create S3 buckets and Lambda functions. Developers must not hold permissions to create those resources directly. Which solution meets the requirement and follows least privilege?
- A. Create a service role with the S3 and Lambda permissions, and let developers pass only that role to CloudFormation.
- B. Attach
AmazonS3FullAccessandAWSLambda_FullAccessto the developers' IAM role for the duration of each deployment window. - C. Have developers deploy with access keys of a shared IAM user that holds the S3 and Lambda permissions.
- D. Add
CAPABILITY_NAMED_IAMto every deployment that creates the S3 buckets and Lambda functions.
-
A pipeline runs
aws cloudformation create-stackon a template that declaresTransform: AWS::Serverless-2016-10-31and anAWS::IAM::RolewithRoleName: orders-worker. The call fails withInsufficientCapabilitiesException. Which TWO changes allow the stack creation to succeed? (Select TWO.)- A. Add
CAPABILITY_IAMto--capabilities. - B. Add
CAPABILITY_NAMED_IAMto--capabilities. - C. Add
--role-arnwith a service role that allowsiam:CreateRole. - D. Add
CAPABILITY_AUTO_EXPANDto--capabilities. - E. Add
--enable-termination-protectionto the call.
- A. Add
Answers
- A. CloudFormation performs the work with the service role's permissions; developers only operate stacks and pass that one role. B and C hand developers (or everyone with the keys) the underlying permissions; D is an acknowledgement for IAM resources, not a permission.
- B, D. The named role requires
CAPABILITY_NAMED_IAM, and creating a stack directly from a template with a transform requiresCAPABILITY_AUTO_EXPAND. A doesn't cover named IAM resources; C supplies permissions, not the acknowledgements; E is unrelated.
7. Change sets
Concept
- A change set previews an update (or a creation or import): CloudFormation compares the submitted template and parameters with the current stack and lists what it would add, modify, remove or import, including whether each modification replaces the resource. Nothing changes until you execute it.
- Several change sets can coexist for one stack; executing one deletes the others because they no longer match the stack.
- Typical use: a reviewer approves the change set (often a manual approval in CodePipeline, see
notes/cicd-guide.md) before production changes apply.
Exam facts
- Commands:
create-change-set(typesUPDATEby default,CREATEfor a new stack, which sits inREVIEW_IN_PROGRESS, andIMPORT, §13),describe-change-set(--include-property-valuesfor before and after values),execute-change-set,delete-change-set,list-change-sets. A change set is ready when its status isCREATE_COMPLETEandExecutionStatusisAVAILABLE; one without differences ends inFAILED. - Reading a change:
Action(Add,Modify,Remove,Import,Dynamic= exact action known only at execution,SyncWithActual= only CloudFormation's metadata changes),Replacement(True,False,Conditional= may replace, decided at execution),Details[].Target.RequiresRecreation(Never,Conditionally,Always),Evaluation(Static,Dynamic),ChangeSource(for exampleDirectModification,ParameterReference,ResourceReference,Automatic). - Change sets don't guarantee success. ⚠️ Changed: pre-deployment validation runs on
create-stack,update-stackandcreate-change-set: property syntax and resource-name conflicts fail the operation before anything is provisioned; change set creation also warns (without failing) about non-empty S3 buckets to delete, service quotas, AWS Config recorder conflicts and non-empty ECR repositories.--disable-validationskips it;describe-eventsshows the results. Older material may say change sets only diff templates and catch nothing before execution. - ⚠️ Changed: drift-aware change sets (
--deployment-mode REVERT_DRIFT) compare desired, previous-deployment and actual state and overwrite out-of-band changes, except AWS-managed properties (such as an Auto Scaling group's desired capacity), write-only properties and immutable properties; a failed execution rolls back to the actual pre-deployment state. Older material may say change sets compare only the new template with the last one and ignore drift. - Nested stacks: the console enables nested change sets by default; the CLI needs
--include-nested-stacks. Execute or delete only from the root change set. execute-change-set --disable-rollbackpreserves successful resources on failure (§8); express mode is chosen when creating the change set (--deployment-config).- Templates with transforms (SAM,
AWS::Include,AWS::LanguageExtensions) are processed in change sets; the processed template is visible in the change set.aws cloudformation deploy= create plus execute a change set. - Parameter Store values (SSM parameter types and
ssmreferences) are read when the change set is created; values changed before execution aren't picked up. - IAM:
cloudformation:CreateChangeSet,DescribeChangeSet,ExecuteChangeSet,DeleteChangeSet; thecloudformation:ChangeSetNamecondition key limits which change sets a user may execute or delete (separation of duties: developers create, approvers execute).
Developer angle
aws cloudformation create-change-set --stack-name orders-prod --change-set-name add-gsi-2026-10-12 \
--template-body file://orders.yaml --parameters ParameterKey=Env,UsePreviousValue=true --capabilities CAPABILITY_IAM
aws cloudformation wait change-set-create-complete --stack-name orders-prod --change-set-name add-gsi-2026-10-12
aws cloudformation describe-change-set --stack-name orders-prod --change-set-name add-gsi-2026-10-12 --output table \
--query "Changes[].ResourceChange.[Action,LogicalResourceId,ResourceType,Replacement]"
aws cloudformation execute-change-set --stack-name orders-prod --change-set-name add-gsi-2026-10-12
# overwrite console edits back to the template values
aws cloudformation create-change-set --stack-name orders-prod --change-set-name revert-console-edits \
--use-previous-template --deployment-mode REVERT_DRIFT
A pipeline gate that stops when a change set would replace anything:
import boto3
cfn = boto3.client("cloudformation")
changes = cfn.describe_change_set(StackName="orders-prod", ChangeSetName="add-gsi-2026-10-12")["Changes"]
replaced = [c["ResourceChange"]["LogicalResourceId"] for c in changes
if c["Type"] == "Resource" and c["ResourceChange"].get("Replacement") in ("True", "Conditional")]
if replaced:
raise SystemExit(f"Change set replaces resources: {replaced}")
Traps
- Trap: "A successful change set guarantees a successful update." → Reality: it previews changes; runtime failures (permissions, quotas, custom resources) can still occur.
- Trap: "
Replacement: Conditionalmeans no replacement." → Reality: the resource may be replaced depending on a value resolved at execution; treat it as possible. - Trap: "Execute a nested change set on its own." → Reality: execute the root change set; it applies the whole hierarchy.
- Trap: "
create-change-setfrom the CLI includes nested stacks." → Reality: add--include-nested-stacks. - Trap: "Other change sets stay usable after one is executed." → Reality: CloudFormation deletes them.
- Trap: "A standard change set shows console edits that the update will overwrite." → Reality: only a drift-aware change set compares against actual state.
Practice questions
-
Before updating a production stack, a developer must see which resources would be replaced without changing anything, and a reviewer must approve before the changes apply. What should the developer do?
- A. Run
update-stackwith--disable-rollback, inspect the stack events, and roll back manually if needed. - B. Run
detect-stack-drifton the stack and review the drift results with the reviewer before updating. - C. Create a change set, review its
Replacementvalues, and execute it after approval. - D. Run
validate-templateon the new template and share the returned capabilities with the reviewer.
- A. Run
-
A root stack contains two nested stacks. A developer runs
aws cloudformation create-change-seton the root stack with an updated child template and sees only anAWS::CloudFormation::Stackmodification, with no detail about the child's resources. Which TWO statements are correct? (Select TWO.)- A. The developer must create a separate change set on each nested stack and execute them one by one.
- B. Adding
--include-nested-stackscreates nested change sets that show the child resources' changes. - C. The nested change sets are executed by executing the root change set.
- D. Change sets don't support stacks that contain nested stacks.
- E. Running
execute-change-seton a nested change set applies only that child's changes.
Answers
- C. A change set lists every change, including
Replacement, and applies nothing until it is executed. A changes the stack immediately; B compares resources with the current template, not with the new one; D checks syntax only. - B, C. The CLI creates nested change sets only with
--include-nested-stacks, and the hierarchy is executed from the root. A and E describe operations started from children, which CloudFormation runs from the root change set instead; D is false.
8. Rollbacks and stack failure options
Concept
- By default a failure undoes the operation: a failed create deletes what it made (
ROLLBACK_COMPLETE), a failed update returns to the last working state (UPDATE_ROLLBACK_COMPLETE). - You can trade automatic cleanup for debuggability (preserve successfully provisioned resources), add alarms that force a rollback (rollback triggers), and repair stacks stuck in failed states (
UPDATE_ROLLBACK_FAILED,DELETE_FAILED).
Exam facts
OnFailurefor creates:ROLLBACK(default),DO_NOTHING,DELETE;--disable-rollbackequalsDO_NOTHINGand can't be combined with--on-failure(change sets use--on-stack-failurewith the same values). StackSets create stack instances withDELETEbehaviour.- Preserve successfully provisioned resources (console) =
--disable-rollbackoncreate-stack,update-stack,execute-change-setanddeploy: CloudFormation stops at the first failure on each provisioning path and leaves the stack inCREATE_FAILEDorUPDATE_FAILED. Then Retry (cause fixed outside the template, e.g. IAM), Update (template fixed; keep preservation selected) or Roll back (rollback-stack, which deletes the stack if it never had a stable state). Not supported when the update replaces (immutable update) the failing resource. - ⚠️ Changed:
--retain-except-on-create(RetainExceptOnCreate) deletes resources that the failed operation newly created during rollback, even if theirDeletionPolicyisRetain. Older material may say the only failure settings are rollback on or off andOnFailure. UPDATE_ROLLBACK_FAILED(a resource changed or deleted outside CloudFormation, missing signals, permissions, quotas, an expired token): the stack can't be updated, only deleted or rolled forward toUPDATE_ROLLBACK_COMPLETE. Fix the cause and runcontinue-update-rollback(missing signals: send them withsignal-resourcefirst); if it can't be fixed, skip resources with--resources-to-skip(logical IDs,NestedStackName.LogicalIdfor children; only resources that failed during the rollback). Skipped resources are markedUPDATE_COMPLETEbut no longer match the template: reconcile before the next update.- ⚠️ Changed:
continue-update-rollback --force-rollbackskips every resource that failed to roll back, across nested stacks, in one call (not combinable with--resources-to-skip; drift detection later reports skipped resources asNOT_CHECKED). Older material may say each resource must be listed in--resources-to-skip. - Rollback triggers: up to 5 CloudWatch alarms (
AWS::CloudWatch::AlarmorAWS::CloudWatch::CompositeAlarm) watched during the create or update and for 0–180 minutes afterwards (MonitoringTimeInMinutes, default 0); an alarm inALARMrolls back the whole operation.INSUFFICIENT_DATAdoesn't trigger unless missing data is treated as breaching; triggers aren't watched during a rollback; a missing alarm fails the operation. cancel-update-stackstops an update inUPDATE_IN_PROGRESSand rolls it back.DELETE_FAILED(non-empty S3 bucket, resource still in use, missing permission): fix and retry, ordelete-stack --retain-resources LogicalIdto finish deleting the stack while keeping the failing resources. ⚠️ Changed:--deletion-mode FORCE_DELETE_STACK(May 2024) force-deletes a stack stuck inDELETE_FAILED, keeping whatever failed to delete. Older material may say--retain-resourcesis the only way out. Retained resources showDELETE_SKIPPEDand keep costing money.
Developer angle
# keep what worked, fix the template, retry in place (or give up with rollback-stack)
aws cloudformation create-stack --stack-name orders-dev --template-body file://orders.yaml --capabilities CAPABILITY_IAM --disable-rollback
aws cloudformation update-stack --stack-name orders-dev --template-body file://orders-fixed.yaml --capabilities CAPABILITY_IAM --disable-rollback
aws cloudformation rollback-stack --stack-name orders-dev
# a stack stuck in UPDATE_ROLLBACK_FAILED
aws cloudformation continue-update-rollback --stack-name orders-prod \
--resources-to-skip LegacyQueue orders-prod-Storage-1A2B3C4D5E6F.ReportsDb
aws cloudformation continue-update-rollback --stack-name orders-prod --force-rollback
# roll back if the 5xx alarm fires during the update or the next 15 minutes
aws cloudformation update-stack --stack-name orders-prod --use-previous-template \
--parameters ParameterKey=Env,UsePreviousValue=true --capabilities CAPABILITY_IAM \
--rollback-configuration "RollbackTriggers=[{Arn=arn:aws:cloudwatch:eu-west-1:111122223333:alarm:orders-5xx,Type=AWS::CloudWatch::Alarm}],MonitoringTimeInMinutes=15"
# a stack stuck in DELETE_FAILED
aws cloudformation delete-stack --stack-name orders-dev --retain-resources UploadsBucket
aws cloudformation delete-stack --stack-name orders-dev --deletion-mode FORCE_DELETE_STACK
- Permissions:
cloudformation:RollbackStack,cloudformation:ContinueUpdateRollback,cloudformation:CancelUpdateStack; make sure the deploying identity can cancel and roll back before starting risky nested updates.
Traps
- Trap: "
continue-update-rollbackrecovers a failed create." → Reality: it is only forUPDATE_ROLLBACK_FAILED; a failed create ends inROLLBACK_COMPLETE(delete) or, with preservation,CREATE_FAILED. - Trap: "
--resources-to-skipcan skip resources that failed in the forward update." → Reality: only resources whose rollback failed. - Trap: "Preservation works for any failing update." → Reality: not when the failing change replaces the resource.
- Trap: "Rollback triggers react to
INSUFFICIENT_DATA." → Reality: onlyALARM, unless the alarm treats missing data as breaching. - Trap: "
FORCE_DELETE_STACKis a faster delete for healthy stacks." → Reality: it is a recovery option for stacks stuck inDELETE_FAILED. - Trap: "
--retain-resourcescleans up the bucket for you." → Reality: the bucket stays, outside the stack, and keeps billing.
Practice questions
-
A stack update failed and the stack is now in
UPDATE_ROLLBACK_FAILED, because an operator deleted an SQS queue that CloudFormation is trying to roll back to. The queue can't be recreated with its original identity. What should the developer do to return the stack to a usable state?- A. Run
update-stackwith the original template and the original parameter values. - B. Run
rollback-stackon the stack to return it to its last known stable state. - C. Run
delete-stackwith--retain-resourcesfor the queue, then create the stack again. - D. Run
continue-update-rollbackand skip the queue with--resources-to-skip.
- A. Run
-
While iterating on a template, a developer sees an EC2 instance fail during creation, and every rollback deletes the instance before its logs can be read. The developer wants to keep the successfully created resources, read
/var/log/cfn-init.log, and then continue with the same stack after fixing the template. Which TWO actions meet these requirements? (Select TWO.)- A. Create the stack with
--disable-rollback(Preserve successfully provisioned resources). - B. Create the stack with
--on-failure DELETEand a longer--timeout-in-minutesvalue. - C. Add
DeletionPolicy: Retainto the instance resource and to its security group resource. - D. Pass
--enable-termination-protectionwhen creating the stack again. - E. After fixing the template, run
update-stackon the stack inCREATE_FAILEDwith failure preservation still selected.
- A. Create the stack with
Answers
- D.
continue-update-rollbackwith skipped resources (or a forced rollback) returns the stack toUPDATE_ROLLBACK_COMPLETE. A isn't allowed inUPDATE_ROLLBACK_FAILED; B works only forCREATE_FAILEDandUPDATE_FAILED; C deletes the stack instead of recovering it. - A, E. Preservation keeps the instance and the stack in
CREATE_FAILED, and an update with preservation retries from that point. B deletes everything; C may keep the instance, but outside a stack that still ends inROLLBACK_COMPLETE, so it can't be continued; D only blocks deletes.
9. Protecting resources and stacks
Concept
Four separate safety nets, each for a different event:
| Mechanism | Protects against | Set where |
|---|---|---|
DeletionPolicy |
A resource being deleted because the stack is deleted, the resource is removed from the template or its condition turns false | Resource attribute in the template |
UpdateReplacePolicy |
The old physical resource being deleted after a replacement during an update | Resource attribute in the template |
| Stack policy | Unintended update actions (modify, replace, delete) on chosen resources during stack updates | JSON document on the stack |
| Termination protection | The whole stack being deleted | Flag on the stack |
Exam facts
DeletionPolicyvalues:Delete(default for most types),Retain(any type; the resource leaves the stack and keeps costing money; the stack endsDELETE_COMPLETEand the resource showsDELETE_SKIPPED),Snapshot(final snapshot, then delete; snapshots remain and are billed),RetainExceptOnCreate(likeRetain, except when the operation that created the resource rolls back).- ⚠️ Changed: the default is
SnapshotforAWS::RDS::DBClusterand forAWS::RDS::DBInstancewithoutDBClusterIdentifier;Deletefor everything else. Older material may sayDeleteis always the default. - ⚠️ Changed:
RetainExceptOnCreateis a fourthDeletionPolicyvalue (the same behaviour can be requested per operation with--retain-except-on-create). Older material may list onlyDelete,RetainandSnapshot. Snapshotis supported forAWS::EC2::Volume,AWS::ElastiCache::CacheCluster,AWS::ElastiCache::ReplicationGroup,AWS::RDS::DBInstance,AWS::RDS::DBCluster,AWS::Redshift::Cluster,AWS::Neptune::DBClusterandAWS::DocDB::DBCluster. AnUpdateReplacePolicy: Snapshoton an unsupported type falls back toDelete.UpdateReplacePolicyvalues:Delete(default),Retain,Snapshot. It applies only when an update replaces the resource (new physical ID), for direct updates and change sets.DeletionPolicydoesn't cover replacement, so set both on stateful resources. For S3 buckets,Deleteunder either policy fails while the bucket holds objects (DELETE_FAILED): empty it first (custom resource, §10) or retain it.- Stack policy: JSON statements with
Effect,Action(Update:Modify,Update:Replace,Update:Delete,Update:*),Principal: "*"(the only value),Resource(LogicalResourceId/Name, wildcards allowed) orNotResource, and an optionalConditiononResourceType(StringEquals, orStringLikefor wildcards). No policy → everything can be updated; once a policy is set, everything not explicitly allowed is denied; an explicitDenywins; one policy per stack, the same for every user. It applies only to updates: it doesn't block stack deletion and isn't IAM access control. - Set a stack policy at creation (
--stack-policy-bodyor--stack-policy-url, up to 16 KB) or later withset-stack-policy(needscloudformation:SetStackPolicy). It can't be deleted, only replaced (an allow-all policy lifts protection);--stack-policy-during-update-bodyoverrides it for one update (the caller also needscloudformation:SetStackPolicy). Use an explicitDenyrather thanNotResourcewithAllow, which doesn't reliably protect a resource. - Termination protection: off by default; enable with
--enable-termination-protectionat creation orupdate-termination-protectionlater (needscloudformation:UpdateTerminationProtection). Set on the root stack and inherited by nested stacks (can't be changed on a child, and an update that removes a nested stack still deletes it). A delete attempt fails and leaves the stack unchanged. It doesn't block updates or resource deletions done through updates. - Service-level deletion protection (
DeletionProtectionEnabledon DynamoDB tables,DeletionProtectionon RDS) complements these: it blocks deletion by anyone, CloudFormation included.
Developer angle
Resources:
OrdersDb:
Type: AWS::RDS::DBInstance
DeletionPolicy: Snapshot
UpdateReplacePolicy: Snapshot
Properties: {Engine: postgres, DBInstanceClass: db.t4g.medium, AllocatedStorage: "50",
MasterUsername: orders_admin, ManageMasterUserPassword: true}
UploadsBucket:
Type: AWS::S3::Bucket
DeletionPolicy: Retain
UpdateReplacePolicy: Retain
Stack policy that blocks replacement or removal of the database and allows everything else:
{
"Statement": [
{"Effect": "Deny", "Action": ["Update:Replace", "Update:Delete"], "Principal": "*",
"Resource": "LogicalResourceId/OrdersDb"},
{"Effect": "Allow", "Action": "Update:*", "Principal": "*", "Resource": "*"}
]
}
aws cloudformation set-stack-policy --stack-name orders-prod --stack-policy-body file://stack-policy.json
aws cloudformation update-stack --stack-name orders-prod --template-body file://orders.yaml --capabilities CAPABILITY_IAM \
--stack-policy-during-update-body file://allow-db-replace.json
aws cloudformation update-termination-protection --stack-name orders-prod --enable-termination-protection
aws cloudformation describe-stacks --stack-name orders-prod --query "Stacks[0].EnableTerminationProtection"
Traps
- Trap: "
DeletionPolicy: Retainalso keeps the old database when an update replaces it." → Reality: replacement is governed byUpdateReplacePolicy. - Trap: "Termination protection stops updates from deleting resources." → Reality: it only blocks deleting the stack; use a stack policy for updates.
- Trap: "A stack policy prevents deleting the stack." → Reality: stack policies cover updates; deletes need termination protection.
- Trap: "Delete the stack policy when it gets in the way." → Reality: it can't be removed; replace it or override it for one update.
- Trap: "Different stack policies per user." → Reality: one policy per stack, applied to everyone; per-user control is IAM.
- Trap: "
DeletionPolicy: Snapshoton an S3 bucket." → Reality: buckets don't support snapshots; useRetain. - Trap: "An RDS instance in the template is deleted with the stack by default." → Reality: without
DBClusterIdentifier, its defaultDeletionPolicyisSnapshot.
Practice questions
-
A production stack contains an
AWS::RDS::DBInstance. If a future template change forces CloudFormation to replace the instance, the old instance's data must be kept as a snapshot. Which attribute meets this requirement?- A.
DeletionPolicy: Snapshot - B.
UpdateReplacePolicy: Snapshot - C. A stack policy that denies
Update:Modifyon the instance - D. Termination protection on the stack
- A.
-
A team must guarantee that nobody can delete the production stack by accident, and that stack updates can never replace or remove the
OrdersTableresource while every other resource stays updatable. Which TWO actions meet these requirements? (Select TWO.)- A. Add
DeletionPolicy: Retainto every resource in the template. - B. Set
UpdateReplacePolicy: RetainonOrdersTable. - C. Enable termination protection on the stack.
- D. Apply a stack policy that denies
Update:ReplaceandUpdate:DeleteonLogicalResourceId/OrdersTableand allowsUpdate:*on everything else. - E. Attach an IAM policy that denies
cloudformation:UpdateStackto all users.
- A. Add
Answers
- B.
UpdateReplacePolicydecides what happens to the old physical resource after a replacement. A applies to deletes, not replacements; C blocks a different action and doesn't snapshot anything; D only prevents deleting the stack. - C, D. Termination protection blocks deleting the stack; the stack policy blocks replacing or removing the table during updates while allowing other changes. A keeps resources after a delete but doesn't stop the stack deletion; B keeps the old table but still allows replacement; E blocks every update.
10. Custom resources
Concept
- A custom resource runs your code (a Lambda function, or a consumer of an SNS topic) whenever the custom resource is created, updated or deleted. CloudFormation sends a JSON request that includes a presigned S3
ResponseURLand waits until your code uploads aSUCCESSorFAILEDresponse there. - Uses: services or settings CloudFormation doesn't support, external systems (on-premises, third-party APIs), lifecycle chores (empty a bucket before deletion, seed a table, look up a value for other resources).
- Alternative for reusable, shared types: registry resource types (private extensions), which support create, read, update, delete and list plus drift detection, without wiring a function into every template.
Exam facts
- Declare as
Custom::MyName(recommended; type names up to 60 characters) orAWS::CloudFormation::CustomResource.ServiceToken(required) is the Lambda function ARN or SNS topic ARN, in the same Region as the stack. Other properties arrive asResourceProperties(plusOldResourcePropertieson updates). - Request fields:
RequestType(Create,Update,Delete),RequestId,StackId,LogicalResourceId,ResourceType,ResponseURL,PhysicalResourceId(updates and deletes). - Response fields:
Status,RequestId,StackId,LogicalResourceId(copied from the request),PhysicalResourceId(required, up to 1 KB, stable),Reason(required withFAILED),Data(read with!GetAtt MyCustom.Key),NoEcho(masksDatareturned byGetAtt). The whole body is limited to 4,096 bytes. PhysicalResourceIdsets update behaviour: the same ID means an in-place update; a new ID means a replacement, and CloudFormation later sendsDeletefor the old ID.- No response → CloudFormation waits until the timeout, then fails the operation. ⚠️ Changed: the
ServiceTimeoutproperty (1–3,600 seconds, default 3,600) sets how long CloudFormation waits. Older material may say a stuck custom resource always blocks the stack for one hour. - Your code must respond on every path, including exceptions and
Deleterequests (answerSUCCESSwhen there is nothing to delete), or deletes and rollbacks hang. - The
cfn-responsemodule (import cfnresponsein Python,require('cfn-response')in Node.js) is available only for inlineZipFilecode; code loaded from S3 must send the HTTPSPUTitself. ⚠️ Changed: CloudFormation zips inlineZipFilecode into a package of up to 4 MB. Older material may say inline code is limited to 4,096 characters. - A function in a VPC needs a route to S3 to send the response (with interface endpoints, allow the CloudFormation-specific S3 buckets).
- Secure dynamic references (
ssm-secure,secretsmanager) aren't supported in custom resource properties; plainssmreferences are resolved before the request is sent. Let the function read secrets with its own role.
Developer angle
Empty a bucket before CloudFormation deletes it (the custom resource references the bucket, so it is deleted first):
Resources:
ArtifactBucket:
Type: AWS::S3::Bucket
EmptyBucketRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- {Effect: Allow, Principal: {Service: lambda.amazonaws.com}, Action: "sts:AssumeRole"}
ManagedPolicyArns: ["arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"]
Policies:
- PolicyName: empty-artifact-bucket
PolicyDocument:
Version: "2012-10-17"
Statement:
- {Effect: Allow, Action: ["s3:ListBucket", "s3:ListBucketVersions"], Resource: !GetAtt ArtifactBucket.Arn}
- {Effect: Allow, Action: ["s3:DeleteObject", "s3:DeleteObjectVersion"], Resource: !Sub "${ArtifactBucket.Arn}/*"}
EmptyBucketFunction:
Type: AWS::Lambda::Function
Properties:
Runtime: python3.13
Handler: index.handler
Timeout: 300
Role: !GetAtt EmptyBucketRole.Arn
Code:
ZipFile: |
import boto3
import cfnresponse
def handler(event, context):
bucket = event["ResourceProperties"]["BucketName"]
physical_id = event.get("PhysicalResourceId", f"empty-{bucket}")
try:
if event["RequestType"] == "Delete":
boto3.resource("s3").Bucket(bucket).object_versions.delete()
cfnresponse.send(event, context, cfnresponse.SUCCESS, {}, physical_id)
except Exception as exc:
cfnresponse.send(event, context, cfnresponse.FAILED, {}, physical_id, reason=str(exc))
EmptyArtifactBucket:
Type: Custom::EmptyBucket
Properties:
ServiceToken: !GetAtt EmptyBucketFunction.Arn
ServiceTimeout: 360 # a little above the function's 300-second timeout
BucketName: !Ref ArtifactBucket
- The function passes a stable physical ID; by default
cfnresponseuses the Lambda log stream name, which can differ between invocations and would turn an update into a replacement. - Debug a hanging custom resource from the function's CloudWatch Logs; the default
Reasonfromcfnresponsepoints to the log stream. - The template creates an unnamed IAM role, so deploy it with
CAPABILITY_IAM.
Traps
- Trap: "Let the exception propagate; CloudFormation sees the Lambda error." → Reality: CloudFormation only reads the S3 response; without one it waits until
ServiceTimeout. - Trap: "Let
cfn-responsechoose the physical ID." → Reality: a changed ID means replacement, and CloudFormation then sendsDeletefor the old one (emptying a bucket, deleting a record). - Trap: "Point
ServiceTokenat the shared function inus-east-1from every Region." → Reality: the function or topic must be in the stack's Region. - Trap: "Return a large configuration document in
Data." → Reality: the response is capped at 4,096 bytes; store it elsewhere and return a pointer. - Trap: "Pass a
{{resolve:secretsmanager:…}}value as a custom resource property." → Reality: secure references aren't supported there; read the secret inside the function. - Trap: "Use
import cfnresponsein code deployed from S3." → Reality: the module is injected only for inlineZipFilecode.
Practice questions
-
A stack creates an S3 bucket that collects build logs. Deleting the stack fails with
DELETE_FAILEDbecause the bucket isn't empty, and the bucket must be deleted together with the stack. Which solution meets this requirement with the LEAST manual effort?- A. Set
DeletionPolicy: Retainon the bucket and delete the bucket by hand later. - B. Add an S3 Lifecycle rule that expires objects after one day, and retry the deletion the next day.
- C. Add a Lambda-backed custom resource that empties the bucket on
Deleterequests. - D. Run
delete-stackwith--deletion-mode FORCE_DELETE_STACKwhenever the first deletion attempt fails.
- A. Set
-
A Lambda-backed custom resource sometimes leaves a stack in
CREATE_IN_PROGRESSfor about an hour before it fails. Logs show that the function times out while calling an external API. Which TWO changes make such failures surface quickly? (Select TWO.)- A. Wrap the handler logic in
try/exceptand send aFAILEDresponse with aReasonbefore the function times out. - B. Set
ServiceTimeouton the custom resource to a value slightly above the function's timeout. - C. Increase the function's memory to 10,240 MB.
- D. Add a
CreationPolicywith aResourceSignalTimeoutofPT5Mto the custom resource. - E. Change the
ServiceTokento the ARN of an SNS topic that the function subscribes to.
- A. Wrap the handler logic in
Answers
- C. The custom resource depends on the bucket, so CloudFormation deletes it first and its
Deletehandler empties the bucket before the bucket is removed. A and D leave the bucket behind; B is slow and unreliable for a stack deletion. - A, B. An explicit
FAILEDresponse ends the wait at once, andServiceTimeoutcaps the wait when no response arrives. C doesn't fix a hanging external call; D isn't supported on custom resources; E only changes the transport, and CloudFormation still waits for the response.
11. Helper scripts and creation policies
Concept
- A resource reaches
CREATE_COMPLETEwhen the service says it exists: an EC2 instance is complete once it is running, not when your application is installed. A creation policy makes CloudFormation wait for success signals instead. - Helper scripts (Python package
aws-cfn-bootstrap, preinstalled in/opt/aws/binon Amazon Linux AMIs; nothing runs unless your user data calls it):cfn-initreads the resource'sAWS::CloudFormation::Initmetadata and installs packages, writes files, runs commands and manages services.cfn-signalreports success or failure to aCreationPolicy(or to a wait condition handle).cfn-hupis a daemon that polls the metadata and runs hooks (typicallycfn-initagain) after a stack update.cfn-get-metadataprints a resource's metadata.
- User data mechanics (run once, as root, base64, logs): see
notes/ec2-mastery-guide.md§3.
Exam facts
AWS::CloudFormation::Initorder inside one config: packages → groups → users → sources → files → commands → services; commands run in alphabetical order of their keys. UseconfigSetsto order several config keys; without--configsets,cfn-inituses thedefaultconfigset or the singleconfigkey.CreationPolicyapplies toAWS::EC2::Instance,AWS::AutoScaling::AutoScalingGroupandAWS::CloudFormation::WaitCondition(and AppStream fleets):ResourceSignal.Count(default 1) andResourceSignal.Timeoutin ISO 8601 (defaultPT5M, maximum 12 hours); Auto Scaling groups addAutoScalingCreationPolicy.MinSuccessfulInstancesPercent(default 100). Too few signals in time, or one failure signal →CREATE_FAILED→ rollback.- Standard user data: install the latest
aws-cfn-bootstrap, runcfn-init -v --stack ${AWS::StackName} --resource X --region ${AWS::Region}, thencfn-signal -e $? --stack … --resource X --region …so thatcfn-init's exit code becomes the signal.cfn-signal's--regiondefaults tous-east-1: always pass the stack's Region. Logs on the instance:/var/log/cfn-init.log,/var/log/cloud-init.log,/var/log/cloud-init-output.log. - Credentials: none required. Without credentials CloudFormation checks stack membership and scopes the call to the instance's own stack; with explicit credentials,
cfn-signalneedscloudformation:SignalResourceand the otherscloudformation:DescribeStackResource. - The instance must reach the CloudFormation endpoint (internet path or VPC endpoint), and S3 for wait condition handles; otherwise signals never arrive and the stack times out. Signal data is limited to 4,096 bytes.
- Rolling updates of Auto Scaling groups:
UpdatePolicy.AutoScalingRollingUpdatewithWaitOnResourceSignals: truewaits for each new instance's signal;PauseTimeis the wait (maximumPT1H, defaultPT5Mwhen waiting for signals),MaxBatchSize(default 1),MinInstancesInService(default 0),MinSuccessfulInstancesPercent,SuspendProcesses.AutoScalingReplacingUpdate(WillReplace: true) builds a new group and keeps the old one on rollback.AutoScalingInstanceRefreshuses an instance refresh instead and doesn't supportcfn-signal(seenotes/ec2-mastery-guide.md§10). cfn-hup:/etc/cfn/cfn-hup.conf(stack,region,intervalin minutes, default 15) plus hook files in/etc/cfn/hooks.d/(triggers=post.update,path=Resources.X.Metadata.AWS::CloudFormation::Init,action=…cfn-init…,runas=root). Without it, metadata changed by a stack update never reaches running instances.- Wait conditions (
AWS::CloudFormation::WaitConditionwith anAWS::CloudFormation::WaitConditionHandlepresigned URL,Timeoutup to 43,200 seconds) coordinate with work outside the stack; for EC2 instances and Auto Scaling groups AWS recommends aCreationPolicy. A wait condition added in an update needs a new handle.
Developer angle
An Auto Scaling group that waits for two healthy instances at creation and for each new instance during rolling updates; the cfn-hup files let running instances reapply metadata changes (SubnetIds and LatestAmiId are parameters as in §2):
Resources:
AppLaunchTemplate:
Type: AWS::EC2::LaunchTemplate
Metadata:
AWS::CloudFormation::Init:
config:
packages: {yum: {nginx: []}}
files:
/etc/cfn/cfn-hup.conf:
content: !Sub |
[main]
stack=${AWS::StackId}
region=${AWS::Region}
interval=5
/etc/cfn/hooks.d/cfn-auto-reloader.conf:
content: !Sub |
[cfn-auto-reloader-hook]
triggers=post.update
path=Resources.AppLaunchTemplate.Metadata.AWS::CloudFormation::Init
action=/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource AppLaunchTemplate --region ${AWS::Region}
runas=root
services:
sysvinit:
nginx: {enabled: "true", ensureRunning: "true"}
cfn-hup: {enabled: "true", ensureRunning: "true", files: [/etc/cfn/cfn-hup.conf, /etc/cfn/hooks.d/cfn-auto-reloader.conf]}
Properties:
LaunchTemplateData:
ImageId: !Ref LatestAmiId
InstanceType: t3.micro
UserData:
Fn::Base64: !Sub |
#!/bin/bash
yum install -y aws-cfn-bootstrap
/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource AppLaunchTemplate --region ${AWS::Region}
/opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource AppGroup --region ${AWS::Region}
AppGroup:
Type: AWS::AutoScaling::AutoScalingGroup
CreationPolicy:
ResourceSignal: {Count: 2, Timeout: PT15M}
UpdatePolicy:
AutoScalingRollingUpdate: {MaxBatchSize: 1, MinInstancesInService: 1, WaitOnResourceSignals: true, PauseTime: PT15M}
Properties:
MinSize: "2"
MaxSize: "4"
VPCZoneIdentifier: !Ref SubnetIds
LaunchTemplate: {LaunchTemplateId: !Ref AppLaunchTemplate, Version: !GetAtt AppLaunchTemplate.LatestVersionNumber}
- Manual signal, for example to let a stuck update rollback continue:
aws cloudformation signal-resource --stack-name orders-prod --logical-resource-id AppGroup --unique-id i-0123456789abcdef0 --status SUCCESS. cfn-initpackage keys areapt,msi,python,rpm,rubygems,yumandzypper;servicesruns through systemd on Amazon Linux 2 and later (aws-cfn-bootstrap2.0-29+), and a service such ascfn-hupneeds a systemd unit file to be managed there.
Traps
- Trap: "
CREATE_COMPLETEon the instance means the app is installed." → Reality: only aCreationPolicypluscfn-signalmakes the stack wait for the app. - Trap: "CloudFormation runs
cfn-initautomatically when metadata exists." → Reality: your user data must call it. - Trap: "Updating
AWS::CloudFormation::Initmetadata reconfigures running instances." → Reality: only ifcfn-hupruns with a hook; otherwise only new instances get it. - Trap: "Set
Timeout: PT24Hfor a slow install." → Reality: the maximum is 12 hours (PauseTimefor rolling updates: 1 hour). - Trap: "Instances in a private subnet with no internet path or endpoint can still signal." → Reality: the signal can't reach CloudFormation; the stack times out and rolls back.
- Trap: "
cfn-signal -e $? --stack … --resource …is enough ineu-west-1." → Reality: without--regionthe signal goes to theus-east-1endpoint and the creation policy times out.
Practice questions
-
A stack launches an EC2 instance whose user data installs an application. The instance reaches
CREATE_COMPLETEbefore the installation finishes, and a dependent resource then fails its health checks. Which solution makes CloudFormation wait until the application is installed?- A. Add a
CreationPolicyto the instance and callcfn-signal -e $?aftercfn-init. - B. Add a
DependsOnattribute from the dependent resource to the instance resource. - C. Pass
--timeout-in-minutes 30to thecreate-stackcall that launches the instance. - D. Add a rollback trigger on the instance's
StatusCheckFailedalarm with a 15-minute monitoring time.
- A. Add a
-
A developer adds a configuration file to the
AWS::CloudFormation::Initmetadata of a launch template and updates the stack. Instances that are already running don't receive the file. Which TWO changes make running instances apply future metadata changes? (Select TWO.)- A. Add
UpdateReplacePolicy: Retainto the launch template. - B. Configure and start the
cfn-hupdaemon on the instances through/etc/cfn/cfn-hup.conf. - C. Run
aws cloudformation signal-resourcefor every instance after each stack update completes. - D. Add a
cfn-huphook file that runscfn-initonpost.updatefor the launch template's metadata path. - E. Increase
ResourceSignal.Countin the creation policy.
- A. Add
Answers
- A. The creation policy holds the instance in
CREATE_IN_PROGRESSuntilcfn-signalreportscfn-init's result. B orders creation but still treats a running instance as complete; C only bounds the total time; D reacts to infrastructure health, not to the installation. - B, D.
cfn-huppolls the stack metadata, and its hook rerunscfn-initwhen the metadata changes. A only matters when the resource is replaced; C sends signals and doesn't apply configuration; E changes how many signals creation waits for.
12. StackSets
Concept
- A StackSet deploys one template as stack instances into many accounts and Regions from one administrator account (the organization's management account or a delegated administrator). Updating the StackSet pushes the change to every instance; instances can differ only by parameter overrides, never by template.
- Permission models: self-managed (you create
AWSCloudFormationStackSetAdministrationRolein the administrator account andAWSCloudFormationStackSetExecutionRolein every target account; any accounts) or service-managed (AWS Organizations trusted access; StackSets creates the roles; targets OUs or the whole organization; automatic deployment to accounts that join a target OU). - A StackSet is a regional resource: it is visible only in the Region where it was created, even though it deploys elsewhere.
Exam facts
- Self-managed: the administration role trusts
cloudformation.amazonaws.comand maysts:AssumeRolethe execution role; the execution role (that exact name unless you pass a customExecutionRoleName) trusts the administrator account and holds CloudFormation permissions plus whatever the template creates. The caller needsiam:PassRoleon the administration role. - Service-managed: the management account activates trusted access; up to 5 delegated administrators (member accounts calling with
--call-as DELEGATED_ADMIN); stacks are never deployed to the management account; targets are OUs (child OUs included) with optional account filters (INTERSECTION,DIFFERENCE,UNION); templates with macros, transforms or nested stacks aren't supported; automatic deployment is set per StackSet with--auto-deployment Enabled=true,RetainStacksOnAccountRemoval=true|false. - Operations:
create-stack-set(the container),create-stack-instances(--accountsfor self-managed or--deployment-targets OrganizationalUnitIds=…, plus--regions),update-stack-set,update-stack-instances --parameter-overrides,delete-stack-instances --retain-stacksor--no-retain-stacks,delete-stack-set(only when it has no instances),describe-stack-set-operation. - Operation preferences:
MaxConcurrentCountorMaxConcurrentPercentage(accounts at a time; percentages round down),FailureToleranceCountorFailureTolerancePercentage(failures per Region before the operation stops),RegionConcurrencyTypeSEQUENTIAL(default) orPARALLEL,RegionOrder,ConcurrencyMode. - ⚠️ Changed:
ConcurrencyMode(November 2023) addsSOFT_FAILURE_TOLERANCE, which keeps the configured concurrency regardless of failures; the defaultSTRICT_FAILURE_TOLERANCEcaps actual concurrency at failure tolerance + 1 and lowers it as failures occur. Older material may say maximum concurrent accounts can never exceed failure tolerance + 1. - Stack instance status:
CURRENT,OUTDATEDorINOPERABLE(a failed delete; remove it withRetainStacksset totrue); per-operation detail:PENDING,RUNNING,SUCCEEDED,FAILED,CANCELLED,FAILED_IMPORT,SKIPPED_SUSPENDED_ACCOUNT. Operation statuses:RUNNING,SUCCEEDED,FAILED,QUEUED,STOPPING,STOPPED. StackSet drift:detect-stack-set-drift. - A failed stack instance creation deletes that stack (StackSets set
OnFailuretoDELETE). - Quotas: 1,000 StackSets per administrator account and 100,000 stack instances per StackSet (both adjustable); 10,000 stack instance operations running at once per Region per administrator account.
- Choose StackSets for "the same baseline in every account and Region" (IAM roles, AWS Config rules, logging buckets, alarms); choose per-account pipelines when applications differ.
Developer angle
# service-managed StackSet for one OU, two Regions in parallel
aws cloudformation create-stack-set --stack-set-name baseline-logging --permission-model SERVICE_MANAGED \
--template-url https://templates-111122223333.s3.eu-west-1.amazonaws.com/logging.yaml \
--auto-deployment Enabled=true,RetainStacksOnAccountRemoval=false --capabilities CAPABILITY_NAMED_IAM
aws cloudformation create-stack-instances --stack-set-name baseline-logging \
--deployment-targets OrganizationalUnitIds=ou-ab12-34cd5678 --regions eu-west-1 eu-central-1 \
--operation-preferences RegionConcurrencyType=PARALLEL,MaxConcurrentPercentage=25,FailureTolerancePercentage=10
aws cloudformation describe-stack-set-operation --stack-set-name baseline-logging \
--operation-id 11111111-2222-3333-4444-555555555555
# self-managed StackSet with a per-instance parameter override
aws cloudformation create-stack-set --stack-set-name app-alarms --template-body file://alarms.yaml --permission-model SELF_MANAGED
aws cloudformation create-stack-instances --stack-set-name app-alarms --accounts 222233334444 555566667777 \
--regions us-east-1 --parameter-overrides ParameterKey=Threshold,ParameterValue=80
- In each target account, the trust policy of
AWSCloudFormationStackSetExecutionRolenames the administrator account ("Principal": {"AWS": "arn:aws:iam::111122223333:root"}); scope its permissions tocloudformation:*plus the resource types the template creates (AWS's sample role grants full admin).
Traps
- Trap: "A service-managed StackSet also covers the management account." → Reality: it never deploys there; manage that account separately.
- Trap: "Any member account can create organization-wide StackSets." → Reality: only the management account or a registered delegated administrator (up to 5).
- Trap: "Delete the StackSet and its stacks in one step." → Reality: delete the stack instances first (retaining or deleting their stacks), then the StackSet.
- Trap: "Use a SAM template with a service-managed StackSet." → Reality: macros, transforms and nested stacks aren't supported there.
- Trap: "Custom execution role names work automatically." → Reality: only
AWSCloudFormationStackSetExecutionRoleis found automatically; pass any other name. - Trap: "
MaxConcurrentCount=10withFailureToleranceCount=0deploys 10 accounts at once." → Reality: in the default strict mode actual concurrency is capped at 1.
Practice questions
-
A company uses AWS Organizations. The same logging resources must be deployed to every existing account and to every account added in the future. Which solution meets these requirements with the LEAST operational overhead?
- A. A CodePipeline pipeline in each account that runs
aws cloudformation deploywhenever the template changes. - B. A StackSet with self-managed permissions and execution roles created in each account.
- C. A nested stack in the management account that declares one child stack for every member account.
- D. A service-managed StackSet that targets the organization root with automatic deployment.
- A. A CodePipeline pipeline in each account that runs
-
A developer creates a StackSet with self-managed permissions in account 111122223333 to deploy into accounts 222233334444 and 555566667777. Stack instance creation fails because the StackSet can't assume a role in the target accounts. Which TWO configurations are required? (Select TWO.)
- A. Activate trusted access with AWS Organizations from the organization's management account.
- B. Register account 111122223333 as a delegated administrator for StackSets with
aws organizations register-delegated-administrator. - C. Create
AWSCloudFormationStackSetExecutionRolein each target account with a trust policy that allows account 111122223333. - D. Pass
--operation-preferences FailureToleranceCount=2tocreate-stack-instances. - E. Create
AWSCloudFormationStackSetAdministrationRolein account 111122223333 with permission to assume the execution role in the target accounts.
Answers
- D. Service-managed StackSets create their own roles and, with automatic deployment, add stack instances when accounts join. A and B need per-account setup and manual follow-up for new accounts; C deploys only into the management account.
- C, E. Self-managed StackSets need the administration role in the administrator account and the execution role, trusting that account, in every target. A and B belong to the service-managed model; D only changes how many failures the operation tolerates.
13. Drift detection and resource import
Concept
- Drift: a resource's actual configuration differs from what the stack expects because someone changed it outside CloudFormation (console, CLI, another tool). Drift detection compares and reports; it never changes anything.
- Import: brings existing resources under a stack's management (adopt console-built resources, move resources between stacks, nest an existing stack) without recreating them.
Exam facts
- Drift detection runs on stacks in
CREATE_COMPLETE,UPDATE_COMPLETE,UPDATE_ROLLBACK_COMPLETEorUPDATE_ROLLBACK_FAILEDand can take several minutes. It is asynchronous:detect-stack-driftreturns aStackDriftDetectionId; polldescribe-stack-drift-detection-status(DetectionStatusDETECTION_IN_PROGRESS,DETECTION_COMPLETEorDETECTION_FAILED, plusStackDriftStatusandDriftedStackResourceCount); read details withdescribe-stack-resource-drifts(--stack-resource-drift-status-filters MODIFIED DELETED). One resource:detect-stack-resource-drift. - Stack drift status:
DRIFTED,IN_SYNC,NOT_CHECKED; resource drift status:IN_SYNC,MODIFIED,DELETED,NOT_CHECKED(unsupported type or skipped by a forced rollback); property differencesADD,REMOVE,NOT_EQUAL, with expected and actual values. - Only property values set explicitly in the template (directly or through parameters) are compared, not service defaults: set a property explicitly to track it. Not compared:
KMSKeyId, write-only values such as passwords, and values that can't be mapped back (Lambda code). Attachments managed from another stack (AWS::EC2::SecurityGroupIngress,AWS::IAM::Policy) can show false drift. Dynamic references aren't drift-checked. - A stack's drift check doesn't include its nested stacks: run it on each nested stack. StackSets:
detect-stack-set-drift. - Permissions:
cloudformation:DetectStackDrift,cloudformation:DetectStackResourceDrift,cloudformation:BatchDescribeTypeConfigurations, plus read permissions for each resource type (for exampleec2:DescribeInstances). - Remediation options: change the resource back, update the template to match and update the stack, overwrite drift with a drift-aware change set (
REVERT_DRIFT, §7), or remove the resource and import it again. Continuous checks: the AWS Config managed rulecloudformation-stack-drift-detection-check(periodic and on configuration changes; needs acloudformationRoleArnwith drift permissions) flagsDRIFTEDstacks as non-compliant. - Resource import (
create-change-set --change-set-type IMPORT --resources-to-import file://…): the template must describe the whole stack plus the imported resources; each imported resource needs aDeletionPolicy(any value) and its identifier (for exampleBucketName); the import can't create, delete or modify other resources; a resource can belong to only one stack per Region; CloudFormation doesn't check that the template matches the real configuration, so run drift detection afterwards. Statuses:IMPORT_IN_PROGRESS,IMPORT_COMPLETE,IMPORT_ROLLBACK_IN_PROGRESS,IMPORT_ROLLBACK_FAILED,IMPORT_ROLLBACK_COMPLETE. Restrict types withcloudformation:ImportResourceTypes. Import supports one level of nesting. - Auto-import:
create-change-set --import-existing-resourcesadopts resources whose static custom name in the template matches an existing resource (needsDeletionPolicyRetainorRetainExceptOnCreate), useful after a deployment failed because a named resource already exists. - Related adoption tools: the IaC generator scans a Region of an account and generates a template from unmanaged resources (the CDK CLI's
cdk migratecan turn it into a CDK app); stack refactoring (create-stack-refactor,execute-stack-refactor) moves resources between stacks or renames logical IDs without replacing them. The classic move: setDeletionPolicy: Retain, remove the resource from the source stack (it stays), then import it into the target stack.
Developer angle
DRIFT_ID=$(aws cloudformation detect-stack-drift --stack-name orders-prod --query StackDriftDetectionId --output text)
aws cloudformation describe-stack-drift-detection-status --stack-drift-detection-id "$DRIFT_ID" \
--query "[DetectionStatus,StackDriftStatus,DriftedStackResourceCount]"
aws cloudformation describe-stack-resource-drifts --stack-name orders-prod --stack-resource-drift-status-filters MODIFIED DELETED \
--query "StackResourceDrifts[].[LogicalResourceId,StackResourceDriftStatus]"
Import a hand-made bucket (import.json, then a template that adds LegacyUploads with a DeletionPolicy):
[{"ResourceType": "AWS::S3::Bucket", "LogicalResourceId": "LegacyUploads",
"ResourceIdentifier": {"BucketName": "orders-legacy-uploads"}}]
aws cloudformation create-change-set --stack-name orders-prod --change-set-name import-legacy-uploads \
--change-set-type IMPORT --resources-to-import file://import.json --template-body file://orders-with-legacy-bucket.yaml
aws cloudformation execute-change-set --stack-name orders-prod --change-set-name import-legacy-uploads
Traps
- Trap: "Drift detection fixes drifted resources." → Reality: it only reports; remediation is a separate step.
- Trap: "A changed service default is detected as drift." → Reality: only properties set explicitly in the template are compared.
- Trap: "Checking the root stack covers its nested stacks." → Reality: run detection on each nested stack.
- Trap: "Import works without touching
DeletionPolicy." → Reality: every imported resource needs one. - Trap: "Import a bucket and add a new queue in the same operation." → Reality: import operations can't create, delete or modify other resources.
- Trap: "A
DRIFTEDstack can't be updated." → Reality: it can; the update may overwrite the out-of-band change.
Practice questions
-
An operator changed the visibility timeout of an SQS queue in the console. The queue belongs to a stack whose template sets
VisibilityTimeout: 60. The developer must find which resources in the stack differ from the template, without changing any resource. What should the developer do?- A. Create a change set from the template that is currently deployed and review the changes it lists.
- B. Run
detect-stack-driftand reviewdescribe-stack-resource-drifts. - C. Run
update-stack --use-previous-templateand read the stack events for the modified resources. - D. Run
validate-templateagainst the deployed template retrieved withget-template.
-
A team created an S3 bucket named
orders-legacy-uploadsand a DynamoDB table by hand, and now wants an existing stack to manage them without recreating them. Which TWO steps are required? (Select TWO.)- A. Add both resources to the stack's template, each with a
DeletionPolicyattribute. - B. Delete the resources and let CloudFormation recreate them with the same names.
- C. Run
detect-stack-drifton the stack with the resource identifiers of both resources. - D. Create a change set of type
IMPORTwhose--resources-to-importlists each resource's identifier, then execute it. - E. Add the resources to the template and point their properties at the existing ARNs with
Fn::ImportValue.
- A. Add both resources to the stack's template, each with a
Answers
- B. Drift detection compares actual property values with the template and changes nothing. A standard change set (A) compares templates only, so it shows no differences; C changes nothing when nothing differs and isn't a drift report; D checks syntax only.
- A, D. Import needs a template that declares the resources with a
DeletionPolicy, and anIMPORTchange set with their identifiers. B destroys data; C only reports drift for resources already in the stack; E reads exports from other stacks.
14. Dynamic references
Concept
- A dynamic reference is a placeholder string
{{resolve:service:…}}inside a resource property. CloudFormation resolves it during stack and change set operations from Systems Manager Parameter Store or Secrets Manager, so sensitive or externally managed values never live in the template, its parameters or version control. - Parameter Store tiers and SecureString encryption, Secrets Manager rotation and KMS details: see
notes/security-encryption-guide.md(its §11 covers dynamic references from the secrets angle).
Exam facts
{{resolve:ssm:name}}or{{resolve:ssm:name:version}}:StringandStringListparameters, public parameters included (latest AMI). Without a version, CloudFormation uses the latest version at each create or update; in theParameterssection a version is required.{{resolve:ssm-secure:name}}or with:version:SecureStringvalues. CloudFormation never stores or returns the plaintext (APIs show the reference itself) and change sets compare only the reference string. Only properties that support it accept it, for exampleMasterUserPasswordofAWS::RDS::DBInstance,AWS::RDS::DBClusterandAWS::Redshift::Cluster,LoginProfile.PasswordofAWS::IAM::User,AuthTokenofAWS::ElastiCache::ReplicationGroup, Directory Service passwords. No public parameters, no other accounts, no labels; if a rollback needs a version that no longer exists, the rollback fails.{{resolve:secretsmanager:secret-id:SecretString:json-key:version-stage:version-id}}: every segment aftersecret-idis optional (defaultAWSCURRENT, wholeSecretStringwithoutjson-key); usable in all resource properties (the value may still surface in the target service); a secret in another account needs its full ARN. AWS recommends versionless references combined with rotation.- Values are fetched only when the resource that contains them is created or updated: rotating a secret or changing a parameter doesn't touch the stack until that resource changes. CloudFormation doesn't track which secret version it used.
- Limits and restrictions: 60 dynamic references per template; secure references aren't supported in custom resources; dynamic references aren't supported in
AWS::CloudFormation::Initmetadata or EC2UserData; a reference can't end with a backslash; keep them out of properties that form a resource's primary identifier (the plaintext may appear in the ID); with transforms they are resolved when the change set executes; no drift detection on them;ssmandssm-securereferences can't read parameters shared from other accounts. - Permissions for the deploying identity or service role:
ssm:GetParametersfor Parameter Store,secretsmanager:GetSecretValuefor Secrets Manager, pluskms:Decryptwhen a customer managed key protects the value. - Prefer them over
NoEchoparameters (§2) for secrets. For RDS, the service can also keep the master password in Secrets Manager itself (ManageMasterUserPassword; seenotes/rds-guide.md).
Developer angle
Resources:
OrdersDb:
Type: AWS::RDS::DBInstance
DeletionPolicy: Snapshot
UpdateReplacePolicy: Snapshot
Properties:
Engine: mysql
DBInstanceClass: db.t4g.micro
AllocatedStorage: "20"
MasterUsername: "{{resolve:secretsmanager:orders/db:SecretString:username}}"
MasterUserPassword: "{{resolve:secretsmanager:orders/db:SecretString:password}}"
WorkerTemplate:
Type: AWS::EC2::LaunchTemplate
Properties:
LaunchTemplateData:
ImageId: "{{resolve:ssm:/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64}}"
InstanceType: t3.micro
SupportUser:
Type: AWS::IAM::User
Properties:
LoginProfile:
Password: "{{resolve:ssm-secure:/orders/support-initial-password:3}}"
PasswordResetRequired: true
Read permissions for the stack's service role:
{"Version": "2012-10-17",
"Statement": [
{"Effect": "Allow", "Action": "ssm:GetParameters",
"Resource": "arn:aws:ssm:eu-west-1:111122223333:parameter/orders/*"},
{"Effect": "Allow", "Action": "secretsmanager:GetSecretValue",
"Resource": "arn:aws:secretsmanager:eu-west-1:111122223333:secret:orders/db-*"}]}
- To pick up a changed secret or parameter, change the resource that holds the reference (or rely on the service's own rotation for the database side); a plain re-run with no resource change doesn't re-read it.
- The template creates an unnamed IAM user, so it needs
CAPABILITY_IAM.
Traps
- Trap: "Rotate the secret and CloudFormation updates the database password." → Reality: CloudFormation reads it only on resource create or update; Secrets Manager rotation keeps the database and the secret in sync.
- Trap: "A
NoEchoparameter is as safe as a dynamic reference." → Reality: the value is still passed as a parameter and leaks wherever it is copied; dynamic references keep it out of the stack. - Trap: "
{{resolve:ssm:…}}reads a SecureString." → Reality:ssmreadsString/StringList; secure strings needssm-securein a supported property. - Trap: "Use
ssm-securein any property." → Reality: only properties that support the pattern; Secrets Manager references work in all properties. - Trap: "Put
{{resolve:secretsmanager:…}}inUserData." → Reality: unsupported; fetch the secret at boot with the instance role. - Trap: "Reference a Parameter Store value shared from another account with
{{resolve:ssm:…}}." → Reality: not supported; use an SSM parameter type with the full ARN, or a Secrets Manager secret ARN.
Practice questions
-
A developer is writing a template for an Amazon RDS for MySQL instance. The master password is stored in AWS Secrets Manager as JSON with
usernameandpasswordkeys. The password must not appear in the template, its parameters or the stack outputs. Which value should theMasterUserPasswordproperty use?- A.
!Ref DbPassword, whereDbPasswordis aNoEchoparameter. - B.
{{resolve:ssm:/orders/db-password}}, where the parameter is aSecureString. - C.
{{resolve:secretsmanager:orders/db:SecretString:password}} - D. A
Mappingsentry that holds the password, read with!FindInMap.
- A.
-
A stack sets an RDS
MasterUserPasswordto{{resolve:secretsmanager:orders/db:SecretString:password}}. A security engineer changed the secret's value in Secrets Manager, but the database still accepts the old password. Which TWO statements are correct? (Select TWO.)- A. CloudFormation re-resolves dynamic references every 15 minutes and updates the database password.
- B. CloudFormation resolves the reference only when it creates or updates the resource that contains it.
- C. Running
detect-stack-driftwill report the password as drifted. - D. Changing the reference to
{{resolve:ssm-secure:/orders/db-password}}makes CloudFormation refresh the value automatically. - E. Configuring Secrets Manager rotation for the secret keeps the database password and the secret in sync without stack updates.
Answers
- C. A Secrets Manager dynamic reference keeps the password out of the template, parameters and outputs. A still passes the password as a parameter value; B fails because
ssmreferences can't readSecureStringparameters; D stores the password in the template. - B, E. Dynamic references are read only during create or update of the resource; rotation changes the database and the secret together. A confuses this with the
cfn-huppolling interval; C is wrong because dynamic references and write-only passwords aren't drift-checked; D behaves the same way as the Secrets Manager reference.
Quick decision rules
- If you see a template larger than 51,200 bytes, think S3 and
--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/S3ObjectVersion, oraws cloudformation package. - If you see a stack in
ROLLBACK_COMPLETE, think delete and recreate. - If you see "the latest AMI in every Region without template edits", think
AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>with the public parameter (or{{resolve:ssm:…}}). - If you see "validate parameter combinations before anything is created", think the
Rulessection. - If you see "resource only in prod", think a condition plus the
Conditionattribute (Fn::IfwithAWS::NoValuefor single properties). - If you see an IAM policy that needs a queue ARN, think
!GetAtt Queue.Arn(Refreturns the URL). - If you see a rejected
!Base64 !Sub, thinkFn::Base64: !Sub |. - If you see independent stacks that must share a VPC ID safely, think
ExportplusFn::ImportValue. - If you see a reusable component inside one deployment, or more than 500 resources, think nested stacks.
- If you see an output needed from another account or Region, think
Fn::GetStackOutput(weak reference). - If you see "export cannot be deleted, in use", think
list-imports, migrate the consumers, then delete. - If you see developers who must deploy resources they can't create directly, think a service role plus
iam:PassRole. - If you see
InsufficientCapabilitieswith a named role, thinkCAPABILITY_NAMED_IAM; with a transform oncreate-stack, thinkCAPABILITY_AUTO_EXPAND(or a change set). - If you see "preview whether the update replaces the database", think a change set and its
Replacementvalue. - If you see nested stacks missing from a CLI change set, think
--include-nested-stacks, executed from the root. - If you see "overwrite console changes back to the template", think a drift-aware change set (
--deployment-mode REVERT_DRIFT). - If you see "keep the resources that worked and retry after fixing", think
--disable-rollback(Preserve successfully provisioned resources). - If you see
UPDATE_ROLLBACK_FAILED, think fix the cause, thencontinue-update-rollback(--resources-to-skipor--force-rollback). - If you see
DELETE_FAILEDon a non-empty bucket, think empty it (custom resource),--retain-resources, or--deletion-mode FORCE_DELETE_STACK. - If you see "roll back when the error alarm fires after the deployment", think rollback triggers with
MonitoringTimeInMinutes. - If you see "keep the data when the stack is deleted", think
DeletionPolicy; "keep the old resource when an update replaces it", thinkUpdateReplacePolicy. - If you see "no update may replace one critical resource", think a stack policy with an explicit
Deny; "no accidental stack deletion", think termination protection. - If you see a step CloudFormation can't do natively, think a Lambda-backed custom resource that always answers the
ResponseURL(and setsServiceTimeout). - If you see "wait until the software on the instance is installed", think
CreationPolicypluscfn-signal -e $? … --region; for running instances that must pick up metadata changes, thinkcfn-hup. - If you see the same baseline in every account of an organization, including future ones, think a service-managed StackSet with automatic deployment.
- If you see a self-managed StackSet that can't assume roles, think
AWSCloudFormationStackSetAdministrationRoleplusAWSCloudFormationStackSetExecutionRole. - If you see "which resources were changed outside CloudFormation", think
detect-stack-driftanddescribe-stack-resource-drifts. - If you see "bring a hand-made resource under a stack", think an
IMPORTchange set with aDeletionPolicyon the resource. - If you see a database password that must not appear in the template, think
{{resolve:secretsmanager:…}}(orssm-securein a supported property).
Changed vs older material
Learn the Current column; older practice material may still show the Older material may say value. After confirmation, the same rows (with verification dates) go to notes/current-facts.md.
| Topic | Older material may say | Current (verified 2026-10-12) | Source |
|---|---|---|---|
| §1 · Where templates must live | Every template must first be uploaded to S3 | Inline TemplateBody up to 51,200 bytes; larger templates up to 1 MB via an S3 TemplateURL; the console uploads local files to a managed bucket |
AWS docs |
| §1 · Template quotas | 200 resources, 60 parameters, 60 outputs, 100 mappings, 450 KB S3 template | 500 resources, 200 parameters, 200 outputs, 200 mappings, 1 MB S3 template (since 2020-10-22) | What's New |
| §1 · Visual template editor | Design templates in Infrastructure Composer (or CloudFormation Designer) | Template studio in the CloudFormation console; the standalone Infrastructure Composer console ends support on 2026-12-07 (full experience in the AWS Toolkit for VS Code) | AWS docs |
| §1 · Waiting for resources | Every stack operation waits for each resource to stabilize | Express mode (--deployment-config '{"mode": "EXPRESS"}') completes once configuration is applied; rollback off by default; not for StackSets |
AWS docs |
§3 · Fn::FindInMap DefaultValue |
Needs the AWS::LanguageExtensions transform |
Works in any template | AWS docs |
| §4 · Name of the reference function | Fn::Ref |
Ref (JSON {"Ref": "Name"}, YAML !Ref Name); there is no Fn:: prefix |
AWS docs |
| §5 · Sharing outputs across accounts and Regions | Outputs can be shared only within one account and Region (exports) | Fn::GetStackOutput reads outputs from other Regions and accounts (RoleArn) without exports, as a weak reference |
AWS docs |
§6 · CAPABILITY_AUTO_EXPAND |
Required for templates with macros or nested stacks | Required for direct create or update from templates with macros or transforms (including macros combined with nested stacks); nested stacks alone don't need it | AWS docs |
| §7 · Validation before deployment | Change sets only diff templates; errors appear during execution | Pre-deployment validation on create-stack, update-stack and create-change-set: property syntax and name conflicts fail early; change sets also warn about quotas, non-empty buckets, Config recorders and ECR repositories; skip with --disable-validation |
AWS docs |
| §7 · Change sets and drift | Change sets compare only the new and previous templates | Drift-aware change sets (--deployment-mode REVERT_DRIFT) compare against actual state and revert drift |
AWS docs |
| §8 · Rollback of newly created resources | Only rollback on or off and OnFailure |
--retain-except-on-create deletes newly created resources on rollback even with DeletionPolicy: Retain |
AWS docs |
§8 · Leaving UPDATE_ROLLBACK_FAILED |
List every resource in --resources-to-skip |
continue-update-rollback --force-rollback skips all resources that failed to roll back, nested stacks included |
AWS docs |
§8 · Leaving DELETE_FAILED |
Only delete-stack --retain-resources |
Also --deletion-mode FORCE_DELETE_STACK (since 2024-05-22) |
AWS docs |
§9 · Default DeletionPolicy |
Delete for every resource |
Delete, except Snapshot for AWS::RDS::DBCluster and for AWS::RDS::DBInstance without DBClusterIdentifier |
AWS docs |
§9 · DeletionPolicy values |
Delete, Retain, Snapshot |
Also RetainExceptOnCreate |
AWS docs |
| §10 · Custom resource wait time | A stuck custom resource always blocks the stack for one hour | ServiceTimeout 1–3,600 seconds (default 3,600) |
AWS docs |
§10 · Inline Lambda code (ZipFile) |
Limited to 4,096 characters | Zipped by CloudFormation into a package of up to 4 MB | AWS docs |
| §12 · StackSets concurrency | Maximum concurrent accounts can never exceed failure tolerance + 1 | ConcurrencyMode SOFT_FAILURE_TOLERANCE keeps the configured concurrency; the default STRICT_FAILURE_TOLERANCE still caps it at tolerance + 1 |
AWS docs |