Skip to content

feat(cloudformation): provision stack set instances as real stacks - #2534

Merged
vieiralucas merged 13 commits into
mainfrom
feat/cfn-stackset-instances
Sep 15, 2026
Merged

vieiralucas merged 13 commits into
mainfrom
feat/cfn-stackset-instances

Conversation

@vieiralucas

@vieiralucas vieiralucas commented Sep 15, 2026

Copy link
Copy Markdown
Member

Summary

Closes #2515. StackSet instances were stubs: CreateStackInstances returned a random OperationId and deployed nothing, DescribeStackInstance always answered CURRENT, ListStackInstances was always empty, and DescribeStackSetOperation reported SUCCEEDED for any id.

Stack instances are now real stacks, provisioned into their target account and region:

  • Typed stack set store (stack_sets.rs) holding the stack set definition, its instances and its operations. Records persisted by older builds in the generic extras store migrate on load.
  • Create/Update/DeleteStackInstances drive CreateStack/UpdateStack/DeleteStack per account x region (StackSet-<name>-<uuid>), with per-instance ParameterOverrides (UsePreviousValue, revert-by-omission), RetainStacks, region order, per-region failure tolerance, the AWSCloudFormationStackSetAccountGate Lambda, and StopStackSetOperation.
  • Operations run in the background as in AWS: the call returns the OperationId and clients poll DescribeStackSetOperation. Instances show OUTDATED/PENDING while they wait. Stacks that provision asynchronously settle on the next read; operations cut short by a restart settle FAILED on load.
  • UpdateStackSet stores the new definition and redeploys all instances, or only the targeted ones (the rest go OUTDATED).
  • SERVICE_MANAGED: OU resolution including nested OUs, AccountFilterType, AccountsUrl, trusted-access check, suspended accounts skipped, CallAs=DELEGATED_ADMIN scoped to service-managed stack sets.
  • ImportStacksToStackSet, CreateStackSet from StackId, ListStackSetAutoDeploymentTargets.
  • DetectStackSetDrift checks each instance's resources against the backing services; ListStackInstanceResourceDrifts reports them. The existence check shared with DetectStackDrift is factored into resource_exists.
  • Modeled errors with their Smithy HTTP codes: StackSetNotFound, StackInstanceNotFound, OperationNotFound, OperationInProgress, OperationIdAlreadyExists, StackSetNotEmpty, NameAlreadyExists, StaleRequest, InvalidOperation, StackNotFound, LimitExceeded.
  • Deleted stack sets stay listable as DELETED and describable by id.

Test plan

  • cargo nextest run -p fakecloud-cloudformation --lib: 408 passed, including 30 new stack set tests (multi-account/region provisioning, overrides, partial updates, tolerance, stop mid-deployment, async settle, restart recovery, OUs/filters/suspension, delegated admin scope and URLs, import, drift, account gate)
  • New e2e cloudformation_stack_sets.rs (AWS SDK against the real binary): queues created per region in the default and a member account, parameter override reaches VisibilityTimeout, update/delete/not-empty/not-found
  • cargo nextest run -p fakecloud-conformance --test cloudformation: stack set lifecycle rewritten as a real sequence
  • conformance run --services cloudformation: 3434/3434 variants pass
  • cargo clippy --workspace --all-targets -- -D warnings, cargo fmt

Surface

  • Docs: website/content/docs/services/cloudformation.md stack sets section rewritten; drift section corrected (it detects deleted resources, it does not always report IN_SYNC); gotcha added for unnamed resources colliding across same-account stacks.
  • No service/op/variant count change, so README, repo description and baseline are unchanged.
  • No introspection endpoint change, so no SDK change.

Summary by cubic

Closes #2515. StackSet instances now provision and manage real CloudFormation stacks in each target account and region instead of returning placeholder operation IDs or always reporting CURRENT. Mutating calls return before deployment completes, so clients must poll the operation status.

StackSet behavior

  • Persists stack sets, instances, operations, and per-target results in a typed store, with migration from legacy extras records.
  • Supports region ordering, failure tolerance, account-gate Lambdas, cancellation, restart recovery, and concurrent-operation validation.
  • Waits for asynchronously provisioning stacks to settle before the next target, recording the instance's stack first so a restart doesn't orphan it.
  • Preserves parameter overrides for skipped, cancelled, or gated targets so later redeployments use them.
  • UpdateStackSet can redeploy selected instances while leaving others OUTDATED; DeleteStackInstances can retain stacks, leaving an undeletable stack INOPERABLE, and DeleteStackSet rejects non-empty sets.
  • Resolves service-managed targets across nested OUs, account filters, AccountsUrl, suspended accounts, trusted access, and delegated administrators.
  • Implements stack imports, creation from StackId, auto-deployment target listing, and caller-account URL resolution.
  • Detects resource drift, reports NOT_CHECKED when no instance can be checked, and rejects reused drift operation IDs with InvalidOperationException.
  • Models AWS-style errors, keeps deleted stack sets describable by ID and listable as DELETED, and adds conformance, end-to-end, and documentation coverage.

Written for commit f2996cb. Summary will update on new commits.

Review in cubic

Stack instances were stubs: CreateStackInstances answered a random
OperationId and deployed nothing, DescribeStackInstance always said
CURRENT, and the operation APIs reported SUCCEEDED for any id.

- Typed stack set store with instances and operations; legacy extras
  records migrate on load
- Create/Update/DeleteStackInstances drive CreateStack/UpdateStack/
  DeleteStack in each target account and region, with per-instance
  parameter overrides, failure tolerance, region order, account gate
  Lambda, and StopStackSetOperation
- UpdateStackSet redeploys all or the targeted instances; the rest go
  OUTDATED
- Operations recorded per target for DescribeStackSetOperation,
  ListStackSetOperations and ListStackSetOperationResults
- SERVICE_MANAGED: OU resolution (nested, filter types, AccountsUrl),
  trusted access check, CallAs=DELEGATED_ADMIN
- ImportStacksToStackSet, CreateStackSet from StackId,
  ListStackSetAutoDeploymentTargets
- DetectStackSetDrift checks instance resources against backing
  services; ListStackInstanceResourceDrifts reports them
- Modeled errors: StackSetNotFound, StackInstanceNotFound,
  OperationNotFound, OperationInProgress, OperationIdAlreadyExists,
  StackSetNotEmpty, NameAlreadyExists, StaleRequest, InvalidOperation

Closes #2515
- Claim each target RUNNING before it deploys so a stop that lands
  during a deployment cancels the remaining targets instead of
  settling STOPPED while they keep deploying
- CallAs=DELEGATED_ADMIN only reaches service-managed stack sets
- Reject a NextToken past the end of the list
…drift start

- Update/DeleteStackInstances reach instances deployed through OUs
  nested below (or above) the requested OUs
- DetectStackSetDrift re-checks for a running operation or reused
  OperationId before recording its operation
- An update deploys an instance that never got a stack instead of
  failing it; suspended accounts are re-checked and skipped again
- A region listed twice deploys once
- TemplateURL, AccountsUrl and StackIdsUrl are read from the caller's
  account, not the management account a delegated admin acts on
- Switching a stack set to SELF_MANAGED drops its AutoDeployment
- Operations deploy in a detached task on the server and the call
  returns the OperationId at once, so a client that drops its request
  cannot strand the operation RUNNING
- Operations interrupted by a restart settle when state loads
- A late outcome from a settled operation no longer writes over
  instances a newer operation owns
- e2e and conformance tests poll operations to completion
…orded

A read landing before the background deployment task started saw a
RUNNING operation with no results and settled it SUCCEEDED, leaving
every target undeployed. Results are now seeded PENDING in the same
locked step that records the operation.
…ettles

- Instances an operation will deploy are recorded OUTDATED/PENDING
  when the operation is recorded, so they are listable while it runs;
  stopping it cancels the ones that never started
- Restore folds finished stacks in before settling interrupted
  operations, so a stack that completed before a restart is not
  reported as failed
…ary, restart states

- AutoDeployment.Enabled=false alone no longer inherits retention
- GetTemplateSummary honors CallAs=DELEGATED_ADMIN for stack sets
- Instances whose targets never started settle CANCELLED on restart,
  matching their results
…ops fail

- Suspension skips deployments only; DeleteStackInstances still removes
  an instance in a suspended account, so its stack set can be emptied
- Suspension is only considered for service-managed stack sets
- An operation interrupted by a restart settles FAILED (or STOPPED)
  with a reason, regardless of failure tolerance
@vieiralucas
vieiralucas requested a lite review from Copilot September 15, 2026 00:56

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

…hecks

The provisioner records a role's physical id as its ARN, but IAM keys
roles by name, so drift detection reported every CloudFormation-created
role as deleted.
@vieiralucas
vieiralucas requested a lite review from Copilot September 15, 2026 01:03

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

…e cases

- Targets skipped, cancelled or gated still take on the requested
  parameter overrides, so a later redeploy uses them
- DetectStackSetDrift reports a reused OperationId as the declared
  InvalidOperationException
- A drift check that could check no instance reports NOT_CHECKED
A stack provisioning in the background (custom resources) returned
RUNNING and never counted against the failure tolerance, so every
remaining target deployed even after it failed. The operation now waits
for such a stack to settle before moving on.
…ERABLE on failed delete

- The instance points at its stack while the stack provisions in the
  background, so a restart during the wait does not orphan it
- A stack that cannot be deleted leaves its instance INOPERABLE
@vieiralucas
vieiralucas merged commit 1682014 into main Sep 15, 2026
157 checks passed
@vieiralucas
vieiralucas deleted the feat/cfn-stackset-instances branch September 15, 2026 02:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature Request: CloudFormation StackSet Instance Provisioning

2 participants