src/config/organization-structure.ts is where you define your entire AWS Organization hierarchy: which Organizational Units exist, which accounts belong to each OU, and which SCPs attach at the root, OU, or account level.
The OrganizationConstruct in the foundation package reads this object and creates or updates OUs, accounts, and SCP attachments on every deploy. It also publishes the resolved OU and account IDs to SSM Parameter Store under /organization/, making them available to StackSet targets in later phases.
Built-in OUs
The starter ships with five pre-configured OUs that match common AWS multi-account patterns:
| OU key | Default name pattern | Purpose |
|---|---|---|
LogArchiveOU | <org>-log-ou | Holds the log archive account that receives centralized CloudTrail logs |
SecurityOU | <org>-security-ou | Holds the security account used as the GuardDuty and Security Hub delegated admin |
DevelopmentOU | <org>-workload-dev-ou | Workload accounts for non-production environments, including the sandbox |
ProductionOU | <org>-workload-prod-ou | Workload accounts for production environments |
SuspendedOU | <org>-suspended-ou | Receives the lockdownSuspendedAccountsSCP to deny all actions in suspended accounts |
You can rename, add, or remove OUs by editing the organizationalUnits map. The OU key (e.g. LogArchiveOU) becomes the SSM parameter suffix (/organization/LogArchiveOUId) and the TypeScript property name on orgVars; changing a key is a compile-time-checked rename.
Options
The OrganizationStructure type has a single required top-level key:
| Option | Type | Required | Description |
|---|---|---|---|
root | OrganizationRootStructure | Yes | The organization root node. Holds SCPs and the OU map. |
root.serviceControlPolicies | ServiceControlPolicy[] | No | SCPs attached at the organization root (apply to every account). Max 10 per target. |
root.organizationalUnits | Record<string, OrganizationalUnitStructure> | No | Map of OU key → OU definition. |
Each OrganizationalUnitStructure entry:
| Option | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Display name of the OU in AWS Organizations. |
serviceControlPolicies | ServiceControlPolicy[] | No | SCPs attached to this OU. Max 10 per target including inherited ones. |
accounts | Record<string, AccountStructure> | No | Map of account key → account definition. |
Each AccountStructure entry:
| Option | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Display name of the account in AWS Organizations. |
email | string | Yes | Root email address for the account. Must be globally unique across all of AWS. |
serviceControlPolicies | ServiceControlPolicy[] | No | SCPs attached to this specific account. Max 10 per target including inherited ones. |
Example
const orgName = landingZoneSettings.organizationName.toLowerCase();
const mailDomain = landingZoneSettings.mailDomain.toLowerCase();
export const organizationStructure = {
root: {
serviceControlPolicies: [
criticalSecurityGuardrailsSCP,
protectTaggedCloudFormationStacksSCP,
denyAllOutsidePrimaryAndSecondaryRegionsSCP,
],
organizationalUnits: {
LogArchiveOU: {
name: `${orgName}-log-ou`,
accounts: {
LogArchiveAccount: {
name: `${orgName}-log-acct`,
email: `aws+log@${mailDomain}`,
},
},
},
SecurityOU: {
name: `${orgName}-security-ou`,
accounts: {
SecurityAccount: {
serviceControlPolicies: [protectSecurityHubConfigurationSCP],
name: `${orgName}-security-acct`,
email: `aws+security@${mailDomain}`,
},
},
},
DevelopmentOU: {
name: `${orgName}-workload-dev-ou`,
accounts: {
SandboxAccount: {
name: 'Sandbox',
email: `sandbox@${mailDomain}`,
},
},
},
ProductionOU: {
name: `${orgName}-workload-prod-ou`,
accounts: {
WorkloadAlphaAccount: {
name: 'Workload Alpha',
email: `aws+workload-alpha@${mailDomain}`,
},
WorkloadBetaAccount: {
name: 'Workload Beta',
email: `aws+workload-beta@${mailDomain}`,
},
},
},
SuspendedOU: {
name: `${orgName}-suspended-ou`,
serviceControlPolicies: [lockdownSuspendedAccountsSCP],
},
},
},
} satisfies OrganizationStructure;
export type LandingZoneOrganizationStructure = typeof organizationStructure;
How it's used
The OrganizationConstruct in Phase 1 (OrganizationStack) reads this object and:
- Creates the AWS Organization if it does not exist
- Creates OUs and accounts as specified
- Attaches SCPs at root, OU, and account levels
- Publishes every OU ID and account ID to SSM under
/organization/<Key>Id
Later stacks read those SSM parameters at synth time via createOrganizationVariables(). The cdk.context.json file caches the lookups. Run pnpm run management:synth after Phase 1 to populate it before deploying Phase 2 and 3.
Things to know
- Account email addresses must be globally unique across all of AWS. The
aws+<alias>@<domain>pattern is a reliable way to generate unique addresses under a single domain. - AWS Organizations limits SCPs to 10 per attachment point (root, OU, or account), counting inherited ones (raised from 5 to 10 in May 2026). Plan SCP layering accordingly.
- Renaming an account or OU key (e.g.
WorkloadAlphaAccount→AppAlphaAccount) changes the SSM parameter name and breaks every stack that referencesorgVars.WorkloadAlphaAccountId. The TypeScript typeLandingZoneOrganizationStructuremakes these renames compile-time errors, so you'll catch them before deploy. - Removing an account from the structure does not close it. AWS Organizations does not allow programmatic account deletion. Move unwanted accounts to
SuspendedOUinstead.