The landing zone deploys through three CDK stacks in a fixed order. Each stack depends on outputs from the previous one, which is why the first deployment requires two steps. Subsequent deployments run all three in one command.
Phase 1: OrganizationStack
This stack runs first because every subsequent stack consumes the organization identifiers it publishes.
| What it creates | Details |
|---|---|
| AWS Organization | Feature set ALL (enables SCPs and other policy types) |
| Organizational Units | As defined in organization-structure.ts |
| AWS accounts | Created and moved to their target OUs |
| Service Control Policies | Authored in service-control-policies/, attached at root/OU/account level |
| GitHub Actions OIDC provider | GitHubActionsOidcConstruct, creates the OIDC provider and GitHubActionsServiceRole, scoped to your repository |
| Alternate contacts | Applied to every account via SetAlternateContactConstruct |
| Marketing email unsubscribe | Unsubscribes account root emails from AWS marketing mail via UnsubscribeMarketingMailsConstruct |
| SSM parameters | Every OU ID and account ID published under /organization/<Key>Id |
After this stack deploys, run pnpm run management:synth to populate cdk.context.json with the SSM-published IDs before deploying Phase 2.
Phase 2: LandingZoneFoundationStack
Prepares the shared infrastructure that StackSets in Phase 3 depend on, and registers the security account as the delegated administrator for the four security services through ProvisionManagementStackSet.
| What it creates | Details |
|---|---|
| StackSet IAM roles | Administration and execution roles for self-managed StackSets |
| Regional asset buckets | S3 buckets in the primary and secondary regions that hold StackSet Lambda assets |
| LogArchiveStackSet | CloudTrail log buckets in the log archive account |
| CentralAlertsStackSet | The cloudtrail-notifications SNS topic in the security account |
| ProvisionManagementStackSet | Management-account CloudTrail, secure defaults, account lifecycle automation, StackSet drift detection, and delegated-admin registration for GuardDuty, Security Hub, Inspector, and Macie |
| Optional ExternalAccessStackSet | Grants a third-party account admin access across the organization (only when externalAdmins is set in settings) |
| Optional OrganizationAccountQuotaStackSet | Requests an Organizations account quota increase from us-east-1 (only when organizationAccountLimit is set) |
The ProvisionManagementStackSet always deploys after LogArchiveStackSet and CentralAlertsStackSet because it imports their CloudTrail bucket and SNS topic ARNs.
Phase 3: LandingZoneAccountProvisioningStack
Rolls out the account baseline across your accounts. The member-account StackSets are service-managed; SecurityHubV2StackSet targets the security account directly, so it runs self-managed.
| StackSet | Targets | Details |
|---|---|---|
SecureDefaultsStackSet | All member accounts (root OU), all configured regions | Deletes default VPCs, enables EBS encryption by default, hardens default security groups, configures SSM service settings, sets S3 Block Public Access and password policy |
CloudTrailStackSet | All member accounts (root OU), primary region | CloudTrail trail with CIS alarms sending to the centralized log archive S3 bucket |
CostControlStackSet | All member accounts (root OU), primary region | AWS Budgets alerts and Cost Anomaly Detection with SNS notifications |
CdkBootstrapStackSet | Development and Production OUs, primary region | CDK bootstrap roles, asset bucket, ECR repository, and a daily asset cleaner for CDK deployments |
ServiceQuotasStackSet | Development and Production OUs, primary region | Quota increase requests: EC2 Elastic IPs (L-0263D0A3) and Lambda concurrent executions (L-B99A9384) |
SecurityHubV2StackSet | Security account only, primary region | Organization-wide Security Hub, GuardDuty, Inspector, and Macie configuration, managed from the delegated administrator account |
The SSM contract
OrganizationStack publishes every OU ID and account ID to SSM Parameter Store under /organization/<Key>Id (e.g. /organization/SecurityAccountId). The createOrganizationVariables() function in the foundation package reads these at CDK synth time via valueFromLookup() and caches them in cdk.context.json.
This is why pnpm run management:synth must be run after Phase 1: the StackSet targets in Phases 2 and 3 use these IDs to know which accounts and OUs to deploy to. Once cdk.context.json is populated and committed, all three phases can deploy in one pipeline run.
Enabling and disabling StackSets
Each StackSet is registered with a landingZone.createStackSet(...) call in its deployment stack. To disable a StackSet, remove (or comment out) its createStackSet block and redeploy. CloudFormation deletes the StackSet and its stack instances. To re-enable it, add the block back.
Key dependencies to respect:
ProvisionManagementStackSetmust deploy afterLogArchiveStackSetandCentralAlertsStackSetSecurityHubV2StackSetdepends onProvisionManagementStackSethaving registered the security account as the delegated administrator for GuardDuty, Security Hub, Inspector, and Macie- The management account is excluded from service-managed StackSets and receives the same baseline (CloudTrail and secure defaults) through
ProvisionManagementStackSet