SAP-C03 Infrastructure as Code: CloudFormation, CDK, Policy as Code and StackSets
Infrastructure as code (IaC) on AWS means defining accounts' resources in version-controlled templates or programs, here AWS CloudFormation and the AWS CDK, so every change is reviewed, repeatable and deployed through the same path. For the SAP-C03 exam, Task 5.1 asks you to design that path for a multi-account, multi-Region estate: choosing declarative templates or imperative CDK code, bootstrapping and protecting stacks, enforcing policy as code at the right layer (CDK aspects, CloudFormation Hooks, SCPs and AWS Config rules), detecting and reverting drift, keeping secrets out of templates with Secrets Manager and Parameter Store, and rolling templates out to many accounts and Regions with StackSets. Building the CI/CD pipeline that runs these deployments is covered in the Task 5.2 lesson.
On this page10 sections
- Declarative vs imperative IaC: CloudFormation templates and AWS CDK code
- Bootstrapping the CDK for multiple accounts and Regions
- Protecting stateful resources during stack updates
- Detecting and remediating drift
- Policy as code layers and CloudFormation Hooks
- CDK aspects and AWS Config rules
- SCPs that keep infrastructure changes on the IaC path
- Managing secrets in infrastructure code
- Multi-account and multi-Region deployments with StackSets
- Rolling out StackSet changes safely and sharing values across Regions
- Choose between CloudFormation templates, template transforms and the AWS CDK, and refactor CDK code without replacing stateful resources
- Bootstrap CDK environments for cross-account pipelines with least-privilege execution policies and lookup-only trust
- Protect stateful resources with stack policies, termination protection, DeletionPolicy and UpdateReplacePolicy, and detect or revert drift
- Place policy as code at the right layer: CDK aspects at synthesis, CloudFormation Hooks at provisioning, SCPs at authorization and AWS Config rules after deployment
- Keep secrets out of templates with dynamic references, generated and rotated secrets, and cross-account and multi-Region secret designs
- Design StackSet deployments: permission models, automatic deployment, delegated administration and operation preferences
- Share values across stacks and Regions without brittle cross-stack exports
Declarative vs imperative IaC: CloudFormation templates and AWS CDK code
Declarative infrastructure as code describes the end state you want and lets an engine work out the steps; imperative IaC describes the steps in a programming language. On AWS the two meet in one place: an AWS CloudFormation template is declarative, and the AWS CDK is imperative code (TypeScript, Python, Java, C#, Go) that synthesizes a CloudFormation template and deploys it as an ordinary stack. So a CDK app keeps everything a CloudFormation estate depends on, such as change sets, drift detection, stack policies, StackSets and Hooks, while giving developers loops, classes, unit tests and reusable components.
| Approach | What it gives you | What it costs or misses |
|---|---|---|
| CloudFormation YAML/JSON | Plain declarative files, no build step, reviewable change sets | Repetition; reuse needs nested stacks, modules or transforms |
AWS::LanguageExtensions transform | Fn::ForEach generates repeated resources, conditions and outputs from a list (for example a CommaDelimitedList parameter), plus Fn::Length and Fn::ToJsonString | Still templates; no tests or classes |
| CloudFormation macro | Any template transformation you can code | A Lambda function you own and operate |
| AWS CDK | Programming-language features, L2/L3 constructs with secure defaults, unit tests; output is CloudFormation | A build (synth) step, bootstrapping, construct-ID discipline |
| AWS SAM | Shorthand template format for serverless resources (Globals) | Still a template; it does not add general programming features |
| SDK or CLI scripts | Full control of call order | No stack, no state, no change sets, no drift detection, no rollback |
Reuse without leaving templates
A team that wants to stay declarative is not stuck with copy-paste. Nested stacks (AWS::CloudFormation::Stack) reuse a whole template as a component; modules package a reusable block of resources as a registry type; and Fn::ForEach from the AWS-managed AWS::LanguageExtensions transform iterates over a collection inside one template. None of these crosses accounts: a nested stack always lives in the parent's account and Region. Reaching many accounts or Regions is the job of StackSets.
Construct levels and escape hatches in the CDK
CDK constructs come in three levels. L1 constructs (CfnBucket, CfnTable) map one to one to CloudFormation resource types and expose every property. L2 constructs (Bucket, Table) wrap an L1 with sensible defaults and helper methods such as grantRead. L3 constructs (patterns) combine several resources. When a CloudFormation property exists but an L2 does not expose it yet, use the escape hatch: get the underlying L1 with node.defaultChild, then set its typed property or call addPropertyOverride. The construct path does not change, so the resource is updated in place.
Logical IDs come from the construct path
The CDK derives each resource's CloudFormation logical ID from its construct ID and the IDs of every parent construct. Moving a resource into a new construct, renaming its ID or swapping an L2 for a fresh L1 therefore changes its logical ID, and CloudFormation reads a new logical ID as "create a new resource and delete the old one". For a stateful resource (a database, a table, a bucket with data) that is replacement. Always read cdk diff before a refactor reaches production. To keep the new code structure without replacement, call overrideLogicalId on the L1 to pin the original ID, or move the resource with CloudFormation stack refactoring (also exposed through the newer, preview-status cdk refactor command), which changes logical IDs or moves resources between stacks without recreating them. A RETAIN removal policy is not a fix: it orphans the old resource while the new, empty one is created. cdk deploy --hotswap is a development shortcut for Lambda code, ECS images and similar assets, it bypasses CloudFormation for them, and it does not apply to resources such as DynamoDB tables.
Bootstrapping the CDK for multiple accounts and Regions
Bootstrapping prepares one environment (an account and Region pair) for CDK deployments by deploying a CloudFormation stack named CDKToolkit. It creates an Amazon S3 staging bucket and an Amazon ECR repository for assets, and IAM roles for publishing files and images, for looking up context, for deploying, and the CloudFormation execution role that actually creates resources. Every environment you deploy to must be bootstrapped: a new Region in an account you already use needs its own cdk bootstrap aws://ACCOUNT/REGION; re-synthesizing or editing the bootstrap stack in another Region does not create anything in the new one.
Cross-account trust
For a pipeline in a tooling account that deploys to workload accounts, you bootstrap the target environments and tell them whom to trust:
| Option | What it grants |
|---|---|
--trust ACCOUNT | The named account may assume the deployment and publishing roles, so it can deploy here |
--trust-for-lookup ACCOUNT | Read-only access through the lookup role, so the account can synthesize apps that call fromLookup methods (for example Vpc.fromLookup) against this environment; it cannot deploy |
--cloudformation-execution-policies ARN | The managed policies attached to the CloudFormation execution role, which limit what stacks can create. The default is AdministratorAccess, and the CLI requires this option whenever --trust is given |
The direction matters: trust is configured in the account being deployed into, never in the pipeline account. A permissions boundary on the pipeline role does not limit the execution role in the target account; the execution policies do.
Changing an existing bootstrap
Re-running cdk bootstrap updates the CDKToolkit stack. When you change the trust lists, list every account that should be trusted, not only the new one; the supplied list replaces the old one. Earlier parameter values are reused by default (--previous-parameters is on), and --no-previous-parameters discards them. A common pattern adds lookup trust for a development account to a production environment while repeating the pipeline account's deployment trust and the restricted execution policy.
Protecting stateful resources during stack updates
CloudFormation offers four separate controls for stateful resources, and each answers a different question: may this change happen, may this stack be deleted, and what happens to the physical resource when it leaves the stack. Mixing them up is the most common design error.
| Control | Scope | What it does | What it does not do |
|---|---|---|---|
| Stack policy | Resources in one stack, checked on every update | Allows or denies Update:Modify, Update:Replace, Update:Delete or Update:* per logical ID or resource type; a denied update fails | Does not stop the stack from being deleted |
| Termination protection | The whole stack | Any delete-stack request fails until someone deliberately turns protection off; updates are unaffected | Does not stop an update from replacing a resource |
DeletionPolicy | One resource | When the resource is removed from the template or the stack is deleted: Delete, Retain, RetainExceptOnCreate or Snapshot | Does not stop the change; it decides the aftermath |
UpdateReplacePolicy | One resource | When an update replaces the resource, decides the fate of the old physical resource: Delete, Retain or Snapshot | Does not stop the replacement |
So "reject any update that would replace the database" is a stack policy (deny Update:Replace and Update:Delete on the cluster's logical ID, allow Update:* on everything else so other resources keep deploying). Once a stack has a policy, anything it does not explicitly allow is denied, which is why the allow statement matters. "Nobody deletes the stack by mistake" is termination protection. "Keep a recoverable copy whenever the database goes away" is Snapshot on both DeletionPolicy (removal or stack deletion) and UpdateReplacePolicy (replacement): CloudFormation takes a final snapshot and then deletes the instance, so nothing keeps billing. Retain keeps the instance running and billing outside the stack. Automated backups are not a substitute: by default they are removed when the DB instance is deleted.
Rollback triggers are a different feature again: they watch CloudWatch alarms during a create or update and roll the operation back if an alarm fires. Manual approval of every change set is safe but scales badly when a pipeline deploys several times a day; a stack policy enforces the same rule on every update automatically.
Detecting and remediating drift
Drift is any difference between a resource's live configuration and the configuration CloudFormation expects from the template and parameters, usually caused by console or API changes made outside the stack. CloudFormation gives you one tool to see drift and one to reverse it.
Drift detection: read-only evidence
Drift detection compares each supported resource with its expected configuration and reports a status per resource (IN_SYNC, MODIFIED, DELETED or NOT_CHECKED) and, for each modified resource, every differing property with its expected value, actual value and difference type. It changes nothing, so it is the right evidence for an auditor. A standard change set does not look at live resources at all; it compares the old template with the new one. The AWS Config managed rule cloudformation-stack-drift-detection-check reports a stack's drift status across accounts, but it is a report, not a repair, and it gives stack-level rather than property-level detail. Stack events show only CloudFormation's own actions.
Why redeploying does not fix drift
A normal stack update acts only on properties that changed between the previous template and the new one. If an engineer edited a security group in the console and the template for that security group did not change, redeploying leaves the console edit in place.
Drift-aware change sets
A drift-aware change set (create the change set with deployment mode REVERT_DRIFT) performs a three-way comparison of the actual state, the previous deployment and the desired template. Its preview shows which live values will be overwritten, and executing it returns drifted properties to their template values, including on resources the new template version does not modify. It deliberately leaves alone properties that AWS services are expected to change. For example, an Auto Scaling group's desired capacity that a scaling policy (AWS::AutoScaling::ScalingPolicy or Application Auto Scaling) manages keeps its actual value when the template does not change it, and the change set reports it as ignored drift. That prevents a reconciliation from collapsing a scaled-out fleet back to the template number. Two limits apply: drift on a property whose change requires replacement is not reverted, and resource types without three-way support fall back to comparing the previous template with the new one.
| Need | Tool |
|---|---|
| Prove what changed, property by property, without touching anything | Drift detection |
| Restore drifted properties to the template, with a preview | Drift-aware change set (REVERT_DRIFT) |
| Bring an existing resource under a stack, keeping its current values | Resource import change set |
| Detect and fix a non-compliant setting whoever made it, continuously, across accounts | AWS Config rule with automatic remediation (next sections) |
Policy as code layers and CloudFormation Hooks
Policy as code means expressing compliance rules as code that tools evaluate automatically, and a sound design places each rule at the layer that can actually enforce it. Four AWS mechanisms act at four different moments, and none of them covers all four.
| Layer | Mechanism | When it acts | Blind spot |
|---|---|---|---|
| Synthesis | CDK aspects | On a laptop or in a build, during cdk synth, before any template reaches AWS | Only CDK apps; anyone deploying a template another way skips it |
| Provisioning | CloudFormation Hooks (Guard, Lambda, Control Tower proactive controls) | Before CloudFormation or Cloud Control API creates or updates a resource | Direct console, CLI or SDK calls to the service bypass it |
| Authorization | SCPs (and RCPs) from AWS Organizations | On every API call by any principal in member accounts, including administrators | Cannot inspect a template's contents; blunt for property-level rules |
| After the fact | AWS Config rules with remediation | When a configuration change is recorded, whoever made it | The resource exists, at least briefly, before it is fixed |
A complete guardrail set often needs three of them together: a Hook so bad templates fail before provisioning, an SCP so even account administrators cannot perform (or undo) a protected action, and a Config rule with remediation for changes made outside the pipeline. The governance design of the organization itself (OU structure, Control Tower landing zones, SCP and RCP strategy) belongs to Task 2.3; this lesson covers using these tools to enforce how infrastructure is deployed.
CloudFormation Hooks: proactive validation before provisioning
A CloudFormation Hook is code that CloudFormation invokes before it provisions, and it can stop a non-compliant resource from ever being created. Hooks come in three forms you can use without writing a registry extension:
- Guard Hooks evaluate declarative AWS CloudFormation Guard rules stored in Amazon S3. No function code to maintain.
- Lambda Hooks call your Lambda function, which returns success or failure. Use them when the logic needs code or external lookups.
- AWS Control Tower proactive controls are AWS-managed Hooks that Control Tower enables on governed OUs.
Targets
A Hook evaluates only the operations it targets: RESOURCE (each resource in a stack operation), STACK (the whole template), CHANGE_SET (when a change set is created) and CLOUD_CONTROL (create, update and delete requests made through the AWS Cloud Control API). Tools that provision through Cloud Control, such as the Terraform awscc provider, are evaluated only when CLOUD_CONTROL is a target. Target filters narrow a Hook to resource types and actions; stack filters (StackNames and StackRoles with include and exclude lists, combined by FilteringCriteria ALL or ANY) narrow which stack operations invoke it. Once a Hook targets Cloud Control API operations, all stack-level filters are ignored, because a Cloud Control request has no stack to match. An exemption for those requests has to live elsewhere: in the Hook's own logic, in a separate Hook, or in a separate account.
FAIL and WARN
The failure mode decides what a failed evaluation does. FAIL stops the operation; WARN lets it proceed and records a warning in the Hook's invocation results. Rolling out a new Hook in WARN first shows exactly which stacks and pipelines it would block, without interrupting anyone; switching to FAIL then enforces it.
Scope: one account, one Region
Hooks are activated and configured per account and per Region. Activating one in the management account does not extend to member accounts, and activating it in one Region leaves every other Region open. To cover an organization, deploy the Hook activation with a service-managed StackSet to every account and every Region in use (or let Control Tower manage proactive controls). Because an account administrator can disable a Hook (SetTypeConfiguration with HookInvocationStatus set to DISABLED) or remove it (DeactivateType), protect it with an SCP, covered below.
CDK aspects and AWS Config rules
CDK aspects and AWS Config rules sit at opposite ends of the deployment: aspects catch a problem before a template exists, and Config catches it after the resource exists.
CDK aspects: validate or fix at synthesis
An aspect is a visitor: you add it to a scope with Aspects.of(scope).add(...), and during the prepare phase of synthesis the CDK calls it for every construct beneath that scope, including L1 resources generated inside third-party L3 constructs that teams cannot edit. An aspect can do one of two things:
- Validate: add an error annotation (
Annotations.of(node).addError(...)) to a non-compliant construct. Errors failcdk synth, so developers see the violation on their laptops before anything is deployed. Warnings do not fail synthesis unless you run with--strict. - Mutate: change the construct, for example set
TracingConfigtoActiveon everyCfnFunctionor turn on versioning for everyCfnBucket. The fix is applied automatically everywhere, with nothing for developers to correct.
Two behaviours trip teams up. First, aspects do not cross Stage boundaries: a Stage is self-contained, so an aspect added to the App never visits stacks created inside Stage subclasses, which is exactly how CDK Pipelines structures production apps. Add the aspect to each Stage (or to the stacks inside it). Second, aspects can be given a priority (AspectPriority.MUTATING runs before READONLY), which controls ordering between aspects but does nothing about the Stage boundary. Aspects only protect CDK apps; pair them with Hooks for anything deployed another way.
AWS Config rules: detect and remediate whoever made the change
AWS Config records configuration changes and evaluates them against rules (AWS managed rules, Guard-based custom policy rules or Lambda-backed custom rules). Because it watches the resource rather than the deployment path, it catches break-glass console edits that Hooks never see. A rule can have automatic remediation that runs an AWS Systems Manager Automation runbook (for example, re-enabling EBS default encryption or removing an open SSH rule). An organization conformance pack deploys a set of rules and their remediations to every account in the organization.
Config rules also offer a proactive evaluation mode. It answers "would a resource with these properties comply?" when something calls it, but by itself it does not block deployments and does not remediate. A rule set to both PROACTIVE and DETECTIVE still lets a non-compliant stack deploy, and then flags the resource afterwards. Blocking needs an enforcement point that acts on the result, such as a Hook. Drift detection on a schedule is not remediation either; it only reports.
SCPs that keep infrastructure changes on the IaC path
Service control policies set the maximum permissions for every principal in the member accounts they are attached to, including account administrators and locally created IAM users and roles, and no one inside the account can edit or detach them. That makes SCPs the tool for rules that must hold even against people with AdministratorAccess. (SCPs do not apply to the management account, one more reason workloads do not run there.)
Make the pipeline the only write path
To keep read access for troubleshooting but force every change through the deployment pipeline, attach an SCP to the production OU that denies write actions unless aws:PrincipalArn matches the pipeline's deployment role. Editing a shared IAM Identity Center permission set would change it in every account that uses it, and a permissions boundary covers only the principals it is attached to; the SCP covers all of them.
Force stacks to use the approved service role
A CloudFormation service role limits what a stack can do only when the stack operation passes it; without one, CloudFormation acts with the caller's own credentials. The condition key cloudformation:RoleARN carries the role passed in the request. An SCP that denies CreateStack, UpdateStack and CreateChangeSet when cloudformation:RoleARN does not equal the approved role's ARN blocks both a different role and no role at all, because a negated condition also matches when the key is absent. Restricting iam:PassRole alone does not force anyone to pass a role.
Protect the guardrails themselves
Account-level controls can be switched off by account administrators. Deny the actions that would disable them, with an exception for the platform role's ARN so the platform team can still manage them:
- Hooks:
cloudformation:SetTypeConfigurationandcloudformation:DeactivateType. - AWS Config:
config:DeleteConformancePack,config:StopConfigurationRecorder(and deleting rules or the recorder). - Logging:
cloudtrail:StopLoggingandcloudtrail:DeleteTrail.
Use aws:PrincipalArn for the exception, not aws:CalledVia: the platform role calls these APIs directly, so a CalledVia condition would lock it out too.
Managing secrets in infrastructure code
A dynamic reference is a placeholder in a CloudFormation template that CloudFormation replaces with a value from AWS Secrets Manager or Systems Manager Parameter Store while it runs the stack operation, so the secret never appears in the template, the parameters or the pipeline configuration.
| Reference | Source | Notes |
|---|---|---|
{{resolve:secretsmanager:secret-id:SecretString:json-key}} | A Secrets Manager secret (optionally a JSON key, version stage or version ID) | Versionless means AWSCURRENT; use the full ARN for a secret in another account |
{{resolve:ssm-secure:name}} | A Parameter Store SecureString | Supported only for specific resource properties; not across accounts |
{{resolve:ssm:name}} | A String or StringList parameter | Plain text, not for secrets |
A NoEcho parameter hides a value in the console and API output, but someone still has to supply it, so it fails a requirement that no person or pipeline handles the secret.
When the value is resolved
CloudFormation resolves a dynamic reference only when it creates or updates the resource that holds it. It does not pin a version, but it also does not re-read the secret on every stack update: an update that changes some other resource leaves the holder untouched, so a changed secret value does not reach it. Either update that resource or, better, let rotation manage credential changes.
Where dynamic references do not work
- Not in EC2
UserDataorAWS::CloudFormation::Initmetadata. Anything placed there would also be readable from the instance metadata. - Secure values (
secretsmanagerandssm-secure) cannot be passed to custom resources.
The pattern in both cases is to pass a pointer (the secret's name or ARN) and let the consumer fetch the value at runtime with its own IAM permissions: the instance profile reads it at boot, and the custom resource's Lambda execution role is granted secretsmanager:GetSecretValue.
Generating, rotating and sharing secrets across accounts and Regions
The safest secret is one that no person ever sees, so a template should generate it, store it and arrange its rotation rather than receive it. Three facts shape every design here.
Generate in Secrets Manager, not Parameter Store
AWS::SSM::Parameter supports only the String and StringList types: CloudFormation cannot create a SecureString parameter. To generate a value from a template, create an AWS::SecretsManager::Secret with GenerateSecretString (length, excluded characters, a template JSON with the generated key), which Secrets Manager creates and encrypts with KMS, then grant the application role permission to read it.
Database credentials: RDS-managed or template-managed
| Design | Fits when |
|---|---|
ManageMasterUserPassword: true on the RDS instance or Aurora cluster | The secret is the master password. RDS generates it, stores it in Secrets Manager and rotates it, with no rotation function or extra resources to maintain. The least operational overhead for master credentials at scale. |
Generated secret + AWS::SecretsManager::SecretTargetAttachment + AWS::SecretsManager::RotationSchedule with HostedRotationLambda | You need a secret RDS does not manage, such as an application login. HostedRotationLambda (which requires the Secrets Manager transform) deploys an AWS-provided rotation function, so there is no custom code. |
Secrets Manager has two rotation strategies. Single user changes the password of one database user in place, so for a short window clients holding the old value can fail to authenticate. Alternating users keeps two users (the second is a clone) and switches the secret between them, using a separate superuser secret to change passwords, so a valid credential always exists. In a template, RotationType selects the strategy (for example PostgreSQLSingleUser or PostgreSQLMultiUser) and MasterSecretArn names the superuser secret that the alternating strategy uses. An application should never be given the master secret just to avoid building its own login.
Sharing a secret with other accounts
For stacks (or applications) in other accounts to read a central secret, three independent conditions must all hold:
- Refer to the secret by its full ARN; a name resolves only in the caller's own account.
- The secret's resource policy allows
secretsmanager:GetSecretValuefor the principals doing the reading. For a dynamic reference that is whoever performs the stack operation, for example each account's CloudFormation service role. - The secret is encrypted with a customer managed KMS key whose key policy allows
kms:Decryptfor those principals. The AWS managed keyaws/secretsmanagercannot be used across accounts, because AWS managed key policies cannot be edited.
Secrets Manager secrets are not shared through AWS RAM, and ssm-secure references cannot reach parameters in other accounts.
Multi-Region secrets
Setting ReplicaRegions on AWS::SecretsManager::Secret creates replica secrets in other Regions of the same account. A replica keeps the same name, its ARN differs only by Region, and Secrets Manager replicates the encrypted value and metadata such as tags and the resource policy. Rotating the primary propagates to every replica, so a standby Region reads a current value from its own endpoint during an outage, with one rotation to manage. Replication does not copy a secret to another account. Each replica is encrypted with a KMS key in its own Region; if you specify none it uses that Region's aws/secretsmanager key, which silently breaks cross-account readers there (Secrets Manager authorization succeeds through the replicated resource policy, then KMS denies the decrypt). Specify a customer managed key in each replica Region whose key policy grants the cross-account readers kms:Decrypt.
Multi-account and multi-Region deployments with StackSets
A CloudFormation StackSet deploys one template as stack instances to many accounts and Regions from a single operation, and its permission model decides who can deploy where.
| Service-managed permissions | Self-managed permissions | |
|---|---|---|
| Setup | Turn on trusted access with AWS Organizations; CloudFormation creates the roles it needs | You create an administration role in the admin account and an execution role in every target account |
| Targets | OUs or the organization root (with account filters INTERSECTION, DIFFERENCE, UNION) | Explicit account IDs, inside or outside an organization |
| New accounts | Automatic deployment: accounts that join a targeted OU get the stack | No automatic deployment; you add accounts and roles yourself |
| Management account | Never deployed to, even when it sits under a targeted root | Can be targeted if it has the execution role |
| Delegation | Up to five member accounts can be delegated administrators | Limited exactly by where execution roles trust the administration role |
Automatic deployment and account removal
With automatic deployment on, an account moved into a targeted OU receives a stack instance, and an account moved out has its stack instance deleted, along with its resources, unless RetainStacksOnAccountRemoval is set to true, which removes the stack instance from the StackSet but keeps the stack and its resources. That is the setting for resources, such as audit logs, that must outlive an account leaving the targeted OUs. Turning automatic deployment off avoids the deletion but stops new accounts from being covered.
The management account gap
Because service-managed StackSets skip the management account by design, a baseline that must also exist there needs separate stacks (or a self-managed StackSet) in the management account. Re-enabling trusted access, account filters or delegation do not change the exclusion.
Delegated administrators
Registering a member account as a StackSets delegated administrator lets a team create and run service-managed StackSets, including automatic deployment, without using the management account; operations from that account run with --call-as DELEGATED_ADMIN. A delegated administrator has full permission to deploy to any account in the organization, and the management account cannot restrict it to particular OUs. When a team must be confined to part of the organization, give it self-managed StackSets and create execution roles only in the accounts it may reach.
Rolling out StackSet changes safely and sharing values across Regions
StackSet operation preferences control how fast an operation spreads and when it stops, and they are evaluated per Region.
| Preference | Meaning |
|---|---|
MaxConcurrentCount / MaxConcurrentPercentage | How many accounts in a Region are processed at once |
FailureToleranceCount / FailureTolerancePercentage | How many accounts may fail in a Region before CloudFormation stops the operation there. A count of 2 stops at the third failure |
ConcurrencyMode | STRICT_FAILURE_TOLERANCE (default) caps actual concurrency at failure tolerance + 1; SOFT_FAILURE_TOLERANCE keeps the configured concurrency and, once the threshold is passed, lets deployments already in progress finish (so a few extra failures are possible) |
RegionConcurrencyType + RegionOrder | SEQUENTIAL finishes one Region before starting the next, in the given order; PARALLEL runs all Regions at once |
| Managed execution | Lets CloudFormation run non-conflicting operations on the StackSet concurrently and queue conflicting ones; it does not speed up a single operation |
Strict mode is why a large Region can crawl: with a tolerance of 2, at most three accounts deploy at once whatever MaxConcurrentCount says. To go faster within an accepted failure budget, either raise the tolerance along with the concurrency or switch to soft mode. When an operation exceeds the tolerance in a Region, CloudFormation marks that Region and the operation failed and does not start the remaining Regions. Instances that already updated keep their new state; StackSets do not roll back completed accounts. With no ordering requirement, PARALLEL shortens a multi-Region update; with a canary Region, SEQUENTIAL with that Region first.
Sharing values across stacks and Regions
CloudFormation exports (Outputs with Export, read by Fn::ImportValue) work only within one account and Region, and while any stack imports an export, the producer cannot change or delete it. Deployment order cannot fix a cross-Region import, and a widely imported export couples every consumer to the producer.
- SSM parameters as the contract. The producer writes values to parameters with the same name in each Region; consumers declare template parameters of type
AWS::SSM::Parameter::Value<String>(or a typed list such asAWS::SSM::Parameter::Value<List<AWS::EC2::Subnet::Id>>). Each stack resolves the parameter in its own Region when it is created or updated, one template works in every Region (ideal for a StackSet), and the producer can change values at any time. - CDK
crossRegionReferences. For a CDK app whose stacks live in different Regions, such as a CloudFront certificate that must be in us-east-1 used by a stack elsewhere, setcrossRegionReferences: trueon both stacks. The CDK writes the value into SSM parameters in the consuming Region through a managed custom resource and reads it there, with no custom code.
Nested stacks are not a sharing mechanism across Regions: a nested stack always deploys in its parent's Region.
Tip. Task 5.1 questions are multi-account scenarios in which several IaC designs would all deploy the infrastructure, and the stated constraint decides: no per-account roles to maintain, block before provisioning versus fix afterwards, no Lambda code, nobody may see the secret, the template must stay identical in every Region, or LEAST operational overhead. Expect near-twin pairs that differ in exactly one property: DeletionPolicy versus UpdateReplacePolicy versus stack policy, drift detection versus a drift-aware change set, Config proactive versus detective mode, Hook FAIL versus WARN, single-user versus alternating-users rotation, strict versus soft StackSet concurrency, --trust versus --trust-for-lookup. Several items are Select TWO or THREE, where each requirement maps to a different control or layer. Symptom items describe what was observed (a refactor that would replace a table, an aspect that never ran, a Hook that missed a Region or a tool, a KMS access denied on a replica, a management account without the baseline) and ask for the root-cause fix.
- The CDK is imperative code that synthesizes declarative CloudFormation, so CDK apps keep change sets, drift detection, Hooks and StackSets. Teams that stay in YAML can iterate with Fn::ForEach from the AWS::LanguageExtensions transform.
- CDK logical IDs come from the construct path: moving or renaming a construct replaces the resource unless you pin the ID with overrideLogicalId or use stack refactoring. Use node.defaultChild escape hatches for properties an L2 lacks.
- Bootstrap every account and Region you deploy to; trust is set on the target with --trust (deploy) or --trust-for-lookup (synthesis lookups only), and --cloudformation-execution-policies replaces the default AdministratorAccess. Re-list all trusted accounts when you change trust.
- Stack policies stop updates, termination protection stops stack deletion, and DeletionPolicy and UpdateReplacePolicy only decide what happens to a resource after it leaves the stack (Snapshot on both keeps a final copy without paying for old instances).
- Drift detection is read-only evidence; a normal redeploy leaves drift on unchanged resources; a drift-aware change set (REVERT_DRIFT) reverts it while keeping AWS-managed values such as scaled desired capacity.
- Hooks block bad templates before provisioning but only for their targets (add CLOUD_CONTROL for Cloud Control API tools) and only in the accounts and Regions where they are activated; roll them out in WARN before FAIL.
- CDK aspects validate or mutate every construct at synth time, including third-party constructs, but do not cross Stage boundaries. Config proactive mode never blocks or remediates; detective rules with SSM Automation remediation fix out-of-band changes.
- SCPs bind every principal in member accounts: use them to make the pipeline role the only writer, to require the approved CloudFormation service role (cloudformation:RoleARN), and to stop administrators disabling Hooks, Config or CloudTrail.
- Use secretsmanager dynamic references (not in UserData, Init metadata or custom resources), generate secrets with GenerateSecretString or ManageMasterUserPassword, and remember CloudFormation cannot create SecureString parameters.
- Cross-account secrets need the full ARN, a resource policy and a customer managed KMS key (also in each replica Region). Service-managed StackSets skip the management account, and strict concurrency caps parallelism at failure tolerance + 1.
Frequently asked questions
What is the difference between AWS CloudFormation and the AWS CDK?
AWS CloudFormation is AWS's declarative infrastructure-as-code service: you write a JSON or YAML template describing resources and CloudFormation creates and updates them as a stack. The AWS CDK is an open-source framework in which you define the same infrastructure in a programming language such as TypeScript or Python; it synthesizes a CloudFormation template and deploys it as a stack. The CDK adds loops, classes, unit tests and higher-level constructs with defaults, while keeping CloudFormation's change sets, drift detection and rollback.
What does cdk bootstrap do, and do I need it in every Region?
cdk bootstrap deploys a CloudFormation stack named CDKToolkit into one account and Region. It creates the Amazon S3 bucket and Amazon ECR repository that hold deployment assets and the IAM roles the CDK uses to publish assets, look up context, deploy and run CloudFormation. Because those resources exist only where they were created, every account and Region pair you deploy to must be bootstrapped, and cross-account pipelines are allowed in with the --trust option on the target environment.
What is the difference between a CloudFormation stack policy and termination protection?
A stack policy controls updates: it allows or denies update actions (modify, replace, delete) on specific resources in a stack, so an update that would replace a protected database fails. Termination protection controls deletion of the whole stack: while it is on, any delete-stack request fails until someone turns it off. Neither decides what happens to a resource that leaves the stack; that is the job of DeletionPolicy and UpdateReplacePolicy.
What is a CloudFormation Hook?
A CloudFormation Hook is a check that CloudFormation runs before it creates, updates or deletes resources, so non-compliant infrastructure can be stopped before it is provisioned. Hooks can evaluate CloudFormation Guard rules stored in S3, call a Lambda function, or come as AWS Control Tower proactive controls. They target resource, stack, change set or Cloud Control API operations, run in FAIL mode (block) or WARN mode (allow with a warning), and are activated per account and Region.
Does AWS Config proactive evaluation block non-compliant CloudFormation deployments?
No. AWS Config proactive evaluation only reports whether a proposed resource configuration would comply when something asks it; it does not block deployments and does not remediate. To stop a non-compliant template you need an enforcement point such as a CloudFormation Hook, and to correct changes after deployment you use detective Config rules with automatic remediation through Systems Manager Automation.
How do I use a Secrets Manager secret in a CloudFormation template without exposing it?
Use a dynamic reference such as {{resolve:secretsmanager:my-secret:SecretString:password}} in the resource property. CloudFormation fetches the value when it creates or updates that resource, so the secret never appears in the template, parameters or outputs. Dynamic references to secure values are not supported in EC2 UserData, AWS::CloudFormation::Init metadata or custom resources; in those places pass the secret's ARN and let the instance or function read it at runtime with its own IAM permissions.
Can CloudFormation create an SSM Parameter Store SecureString parameter?
No. The AWS::SSM::Parameter resource supports only String and StringList parameters, so a template that tries to create a SecureString fails. To have a template generate and store an encrypted secret, create an AWS::SecretsManager::Secret with GenerateSecretString, or for an RDS or Aurora master password set ManageMasterUserPassword so the database service creates and rotates the secret itself.
What is the difference between service-managed and self-managed permissions in CloudFormation StackSets?
Service-managed StackSets use AWS Organizations trusted access: CloudFormation creates the roles it needs, you target OUs or the root, new accounts can receive stacks automatically, and the management account is never targeted. Self-managed StackSets require you to create an administration role and an execution role in each target account, target explicit account IDs, and offer no automatic deployment, but they let you limit exactly which accounts an administrator can reach.
Source
This lesson covers the "Operational Excellence and Automation" domain of the official SAP-C03 exam guide. Vendors revise their guides — check the source for the current version.
- AWS Certified Solutions Architect – Professional (SAP-C03) exam guide — Amazon Web Services
Sign up free to mark lessons complete, bookmark topics and track your exam readiness.