AWS CDK Landing Zone

Stacks

The three CDK Landing Zone deployment phases, OrganizationStack, LandingZoneFoundationStack, and LandingZoneAccountProvisioningStack.

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.

Mermaid diagram loading.

Phase 1: OrganizationStack

This stack runs first because every subsequent stack consumes the organization identifiers it publishes.

What it createsDetails
AWS OrganizationFeature set ALL (enables SCPs and other policy types)
Organizational UnitsAs defined in organization-structure.ts
AWS accountsCreated and moved to their target OUs
Service Control PoliciesAuthored in service-control-policies/, attached at root/OU/account level
GitHub Actions OIDC providerGitHubActionsOidcConstruct, creates the OIDC provider and GitHubActionsServiceRole, scoped to your repository
Alternate contactsApplied to every account via SetAlternateContactConstruct
Marketing email unsubscribeUnsubscribes account root emails from AWS marketing mail via UnsubscribeMarketingMailsConstruct
SSM parametersEvery 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 createsDetails
StackSet IAM rolesAdministration and execution roles for self-managed StackSets
Regional asset bucketsS3 buckets in the primary and secondary regions that hold StackSet Lambda assets
LogArchiveStackSetCloudTrail log buckets in the log archive account
CentralAlertsStackSetThe cloudtrail-notifications SNS topic in the security account
ProvisionManagementStackSetManagement-account CloudTrail, secure defaults, account lifecycle automation, StackSet drift detection, and delegated-admin registration for GuardDuty, Security Hub, Inspector, and Macie
Optional ExternalAccessStackSetGrants a third-party account admin access across the organization (only when externalAdmins is set in settings)
Optional OrganizationAccountQuotaStackSetRequests 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.

StackSetTargetsDetails
SecureDefaultsStackSetAll member accounts (root OU), all configured regionsDeletes default VPCs, enables EBS encryption by default, hardens default security groups, configures SSM service settings, sets S3 Block Public Access and password policy
CloudTrailStackSetAll member accounts (root OU), primary regionCloudTrail trail with CIS alarms sending to the centralized log archive S3 bucket
CostControlStackSetAll member accounts (root OU), primary regionAWS Budgets alerts and Cost Anomaly Detection with SNS notifications
CdkBootstrapStackSetDevelopment and Production OUs, primary regionCDK bootstrap roles, asset bucket, ECR repository, and a daily asset cleaner for CDK deployments
ServiceQuotasStackSetDevelopment and Production OUs, primary regionQuota increase requests: EC2 Elastic IPs (L-0263D0A3) and Lambda concurrent executions (L-B99A9384)
SecurityHubV2StackSetSecurity account only, primary regionOrganization-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:

  • ProvisionManagementStackSet must deploy after LogArchiveStackSet and CentralAlertsStackSet
  • SecurityHubV2StackSet depends on ProvisionManagementStackSet having 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