StackSets deploy one CloudFormation template to many accounts and regions. With service-managed permissions they integrate with AWS Organizations: new accounts in an OU get the stack automatically. That makes them ideal for account baselines, and dangerous if a bad change rolls to 300 accounts at once.
Permission models
| Service-managed | Self-managed | |
|---|---|---|
| Who creates execution roles | CloudFormation, through Organizations | You, in every target account |
| Targets | OUs (and accounts within them) | Account IDs |
| Auto-deploy to new accounts | Yes | No |
| Use when | Org-wide baselines | Targets outside the org, or specific trust needs |
Enable trusted access once from the management account, and register a delegated administrator so day-to-day work happens outside the management account:
aws cloudformation activate-organizations-access
aws organizations register-delegated-administrator --account-id 444444444444 --service-principal member.org.stacksets.cloudformation.amazonaws.com
From the delegated admin account, pass --call-as DELEGATED_ADMIN on StackSet commands.
Create a StackSet
aws cloudformation create-stack-set --stack-set-name baseline-security \
--template-body file://baseline-security.yaml \
--permission-model SERVICE_MANAGED \
--auto-deployment Enabled=true,RetainStacksOnAccountRemoval=false \
--capabilities CAPABILITY_NAMED_IAM \
--managed-execution Active=true \
--call-as DELEGATED_ADMIN
--managed-execution Active=true lets CloudFormation run non-conflicting operations concurrently instead of queuing them.
Deploy with guardrails
aws cloudformation create-stack-instances --stack-set-name baseline-security \
--deployment-targets OrganizationalUnitIds=ou-abcd-sandbox \
--regions eu-west-1 eu-central-1 \
--operation-preferences RegionConcurrencyType=SEQUENTIAL,MaxConcurrentPercentage=10,FailureTolerancePercentage=0 \
--call-as DELEGATED_ADMIN
| Preference | Meaning | Safe default |
|---|---|---|
MaxConcurrentCount/Percentage | Accounts per region at the same time | 10% |
FailureToleranceCount/Percentage | Failures allowed per region before the operation stops | 0 for risky changes |
RegionConcurrencyType | Regions one after another or in parallel | SEQUENTIAL |
RegionOrder | Which region goes first | Least critical first |
Roll out by ring: sandbox OU, then non-prod OU, then prod OU, as separate operations you check between.
Updates and overrides
aws cloudformation update-stack-set --stack-set-name baseline-security \
--template-body file://baseline-security.yaml --capabilities CAPABILITY_NAMED_IAM \
--deployment-targets OrganizationalUnitIds=ou-abcd-nonprod --regions eu-west-1 \
--operation-preferences MaxConcurrentPercentage=20,FailureTolerancePercentage=0 \
--call-as DELEGATED_ADMIN
Per-account or per-OU parameter values:
aws cloudformation update-stack-instances --stack-set-name baseline-security \
--deployment-targets OrganizationalUnitIds=ou-abcd-prod --regions eu-west-1 \
--parameter-overrides ParameterKey=AlarmEmail,ParameterValue=prod-oncall@example.com \
--call-as DELEGATED_ADMIN
Warning
An update that omits
--deployment-targetsand--regionsapplies to every instance. For a big StackSet that is your whole estate in one operation.
When instances fail
aws cloudformation list-stack-set-operations --stack-set-name baseline-security --call-as DELEGATED_ADMIN --max-results 5
aws cloudformation list-stack-set-operation-results --stack-set-name baseline-security \
--operation-id <op-id> --call-as DELEGATED_ADMIN \
--query "Summaries[?Status=='FAILED'].[Account,Region,StatusReason]" --output table
aws cloudformation list-stack-instances --stack-set-name baseline-security --call-as DELEGATED_ADMIN \
--filters Name=DETAILED_STATUS,Values=FAILED --output table
| Status reason | Cause | Fix |
|---|---|---|
ResourceLogicalId: Already exists | Named resource (IAM role, bucket) already in the account | Import it, rename, or delete the stray one |
Account ... should have 'AWSCloudFormationStackSetExecutionRole' | Self-managed model without execution role | Deploy the role |
| Region disabled / not opted in | Target region not enabled in that account | Enable region or remove it from targets |
INOPERABLE instance | Stack deleted outside StackSets, or account left org | delete-stack-instances --retain-stacks, then recreate |
| SCP denies an action | Guardrail blocks the template | Exclude the StackSet execution role in the SCP or change the template |
Drift
aws cloudformation detect-stack-set-drift --stack-set-name baseline-security --call-as DELEGATED_ADMIN
aws cloudformation describe-stack-set --stack-set-name baseline-security --call-as DELEGATED_ADMIN \
--query "StackSet.StackSetDriftDetectionDetails"
Fix drift by redeploying the instance, not by editing in the console.
Prevent it next time
- Keep StackSet templates in Git; changes go through a pipeline that deploys to the sandbox OU first.
- Zero failure tolerance and small concurrency for anything touching IAM or networking.
- Avoid hard-coded resource names in baseline templates; let CloudFormation generate them or include the account ID.
- Delegated admin for StackSets, so the management account is barely used.