AWS CDK Landing Zone

Customize StackSets

Enable, disable, or retarget landing zone StackSets by editing the two provisioning stack files.

The landing zone rolls out its account baseline through CloudFormation StackSets. Which StackSets deploy, and to which accounts, OUs, and regions, is controlled by createStackSet calls in two files under src/stacks/. The StackSet implementations themselves live in src/stacksets/ and are separate from the targeting logic.

For a full inventory of what each StackSet deploys, see the StackSets reference.

The two stack files that control deployment

FilePhaseGoverns
src/stacks/landing-zone-foundation-stack.ts2LogArchiveStackSet, CentralAlertsStackSet, ProvisionManagementStackSet
src/stacks/landing-zone-account-provisioning-stack.ts3SecureDefaultsStackSet, CloudTrailStackSet, CostControlStackSet, CdkBootstrapStackSet, ServiceQuotasStackSet, SecurityHubV2StackSet

Do not edit the StackSet implementation files in src/stacksets/ to change targeting. Only edit the target and regions arguments in the stack files above.

Disabling a StackSet

To disable a StackSet, comment out or remove its createStackSet block from the relevant stack file and redeploy. CloudFormation deletes the StackSet and removes its stack instances from all target accounts.

// Disable CostControlStackSet by commenting out the block:
// landingZone.createStackSet(this, {
//   name: 'CostControlStackSet',
//   description: 'Set up cost control measures for organization accounts',
//   stackSetStack: new CostControlStackSetStack(this, 'CostControlStackSetStack', {
//     securityAccountEmail,
//   }),
//   target: StackSetTarget.fromOrganizationalUnits({
//     regions: [this.region],
//     organizationalUnits: [orgVars.RootOUId],
//   }),
// });

To re-enable it, uncomment the block and redeploy.

Dependencies to respect:

  • ProvisionManagementStackSet declares an explicit dependencies: [logArchiveStackSet, centralAlertsStackSet], so it always deploys after both. It imports the centralized CloudTrail delivery bucket from the log archive account and the CloudTrail notification topic from the security account, which those two StackSets create.
  • SecurityHubV2StackSet (Phase 3) relies on the delegated-administrator registrations that ProvisionManagementStackSet (Phase 2) creates for Security Hub, GuardDuty, Inspector, and Macie. Because the two run in separate phases, the phase split enforces this ordering rather than a dependencies entry, so keep ProvisionManagementStackSet enabled whenever SecurityHubV2StackSet is. SecurityHubV2StackSet receives its GuardDuty and Macie member accounts directly (resolved from your organization structure), so it owns member association itself.
  • SecureDefaultsStackSet covers member accounts through a service-managed StackSet, while the management account receives the same hardening from the SecureDefaultsConstruct inside ProvisionManagementStackSet. Disabling one without the other leaves a gap in coverage.

Changing OU and account targets

Each createStackSet call takes a target that controls where the StackSet deploys. The OU and account IDs come from orgVars (the SSM-published IDs of your organization structure). Two targeting patterns are available:

Target whole OUs or the root (service-managed):

target: StackSetTarget.fromOrganizationalUnits({
  regions: [this.region],
  organizationalUnits: [orgVars.RootOUId],
}),

New accounts that join the targeted OU automatically receive the StackSet instance; accounts that leave have it removed. Swap orgVars.RootOUId for any OU ID from your organization structure, for example [orgVars.DevelopmentOUId, orgVars.ProductionOUId] to limit a StackSet to workload accounts only.

Narrow to specific accounts within an OU (service-managed with intersection):

target: StackSetTarget.fromOrganizationalUnits({
  organizationalUnits: [orgVars.RootOUId],
  intersectionAccounts: [orgVars.SecurityAccountId],
  regions: [this.region, ...secondaryRegions],
}),

The LogArchiveStackSet and CentralAlertsStackSet use this pattern: they target the root OU but intersect to a single account, so only the log archive or security account receives the StackSet. (SecurityHubV2StackSet and ProvisionManagementStackSet instead target their account directly with fromAccounts, which makes them self-managed.)

Target explicit accounts (self-managed):

target: StackSetTarget.fromAccounts({
  regions: [this.region, ...secondaryRegions],
  accounts: [settings.managementAccountId],
}),

Use this for StackSets that must reach the management account. Service-managed StackSets cannot deploy to the management account, so these require deploymentType: landingZone.selfManagedDeploymentType. Self-managed StackSets need the StackSet IAM roles that LandingZoneFoundationConstruct creates.

Changing regions

The regions array on each target controls which regions receive a StackSet instance. Most StackSets deploy only to the primary region ([this.region]). Region-sensitive StackSets (secure defaults and central alerts) also cover secondaryRegions from your settings.

To add a region to a specific StackSet:

target: StackSetTarget.fromOrganizationalUnits({
  regions: [this.region, ...secondaryRegions, 'ap-southeast-1'],
  organizationalUnits: [orgVars.RootOUId],
}),

Adding a region that isn't in landing-zone-settings.ts's secondaryRegions is valid, but the region guardrail SCP may block API calls in that region unless you also add it to secondaryRegions. See Landing Zone Settings for how regions are configured.

Preview and deploy

After any targeting change, preview the diff first:

pnpm run management:diff

Then deploy:

pnpm run management:deploy

CloudFormation handles adding and removing StackSet instances as the target changes. Removing an OU from a target removes stack instances from all accounts currently in that OU; adding one deploys instances to all accounts already there.