You add an OU when you need a distinct governance boundary: a separate SCP posture, a clear cost allocation boundary, or a new team domain that shouldn't share an OU with existing workloads. This guide covers flat OUs and nested OUs, SCP attachment, and what to check for StackSet targeting after the change.
Steps
1. Add the OU in organization-structure.ts
Open src/config/organization-structure.ts and add a new key to root.organizationalUnits. The key becomes the SSM parameter suffix and the TypeScript property name on orgVars. Keep it PascalCase and end it with OU by convention:
DataOU: {
name: `${orgName}-data-ou`,
accounts: {
DataLakeAccount: {
name: 'Data Lake',
email: 'aws+data-lake@example.com',
},
},
},
The name is the display name shown in the AWS Organizations console. You can leave accounts empty if you only need the OU structure now and plan to add accounts later.
Nested OUs
AWS Organizations supports nested OUs. The landing zone's type system supports arbitrary nesting. Add a child OU by nesting an OrganizationalUnitStructure inside the parent's accounts-adjacent structure. In practice, each nested OU key still maps to the same OrganizationalUnitStructure shape:
WorkloadsOU: {
name: `${orgName}-workloads-ou`,
organizationalUnits: {
FrontendOU: {
name: `${orgName}-workloads-frontend-ou`,
accounts: {
FrontendProdAccount: {
name: 'Frontend Production',
email: 'aws+frontend-prod@example.com',
},
},
},
BackendOU: {
name: `${orgName}-workloads-backend-ou`,
accounts: {},
},
},
},
Note that deep nesting adds complexity to SCP layering (inherited SCPs count toward the 5-per-target limit) and makes StackSet targeting more involved. I'd keep nesting shallow, as two levels is almost always enough.
2. Attach SCPs to the new OU (optional)
If the new OU needs guardrails beyond those inherited from the root, import your SCP constants and add them to serviceControlPolicies on the OU definition. See Create a Custom SCP for authoring a new SCP, and Service Control Policies for the four shipped guardrails.
DataOU: {
name: `${orgName}-data-ou`,
serviceControlPolicies: [denyS3PublicAccessSCP],
accounts: {
DataLakeAccount: {
name: 'Data Lake',
email: 'aws+data-lake@example.com',
},
},
},
AWS Organizations enforces a hard limit of 10 SCPs per attachment point, counting SCPs inherited from parent OUs. If the root already has 2 SCPs attached, this OU can receive at most 8 more before hitting the limit. Plan accordingly: group related guardrails into a single SCP rather than stacking many small ones.
3. Consider StackSet targeting
The Phase 3 StackSets in LandingZoneAccountProvisioningStack target OUs by ID at synth time. Most StackSets target orgVars.RootOUId, which covers every account in the organization including accounts in your new OU. Those StackSets will automatically include new accounts added to the new OU, with no stack changes needed.
If you want finer-grained targeting (for example, the ServiceQuotasStackSet currently targets only DevelopmentOUId and ProductionOUId), open src/stacks/landing-zone-account-provisioning-stack.ts and add orgVars.DataOUId to that StackSet's organizationalUnits array. See Customize StackSets for the full targeting options.
4. Preview the change
pnpm run management:diff
Expect to see a new AWS::Organizations::OrganizationalUnit resource and the corresponding SSM parameter (/organization/DataOUId) in the OrganizationStack diff. If you added accounts, you'll also see AWS::Organizations::Account resources.
5. Deploy
pnpm run management:deploy
The organization stack deploys first, creating the OU and publishing its ID to SSM. The landing-zone stacks follow and update any StackSets that need re-targeting.
6. Verify
Confirm the OU exists in AWS Organizations:
aws organizations list-organizational-units-for-parent \
--parent-id $(aws organizations list-roots --query 'Roots[0].Id' --output text) \
--query 'OrganizationalUnits[?Name==`your-org-data-ou`]'
Confirm the SSM parameter was published:
aws ssm get-parameter --name /organization/DataOUId --query Parameter.Value
What happens on deploy
OrganizationStack calls the AWS Organizations API to create the OU under the root (or under its parent OU for nested structures), creates any accounts defined inside it, and attaches the specified SCPs. Every new OU ID is published to SSM under /organization/<Key>OUId.
If any Phase 3 StackSet targets the new OU explicitly, CloudFormation updates the StackSet's organizational unit targets and automatically deploys stack instances into any accounts already in that OU. Accounts added to the OU after the StackSet is deployed receive their instances automatically through the service-managed targeting mechanism. That's the main reason to prefer OU-targeted StackSets over explicit account lists.
Renaming an OU key (e.g. DataOU to DataPlatformOU) changes the SSM parameter name and breaks every reference to orgVars.DataOUId. The TypeScript type LandingZoneOrganizationStructure surfaces these as compile errors, so you'll catch them before deploy. Rename carefully and update all references in the stack files at the same time.