Your AWS organization has five or six accounts, and the count is about to double. A second product team wants accounts, staging needs a home, and your first enterprise client asks for a dedicated one. Next come service control policies (SCPs), the organization-level guardrails that cap what accounts can do.
Before you attach anything, decide where each account lives. The AWS organizational unit tree decides which accounts share which guardrails, and it is cheap to draw but slow to change. Moving an account later puts it under a different policy chain and can delete the StackSet stacks its old OU deployed.
Most first drafts copy the org chart, one OU per team, and break once two teams need different guardrails. Start from controls instead, and the tree stays stable as you grow.
What is an AWS organizational unit?
An AWS organizational unit (OU) is a container that groups accounts, and other OUs, under the organization root. Attach a policy to an AWS Organizations OU once and every account below it inherits that policy.
The AWS Organizations user guide sets a few fixed rules for the tree:
- Each organization has one root, which Organizations creates for you.
- OUs nest up to 5 levels below the root.
- Each account sits directly in the root or in exactly one OU, never in two.
- OU names must be unique within their parent, so two
prodOUs under different parents are fine.
The management account, the one that created the organization, can sit anywhere in the tree. SCPs never restrict it, so keep workloads out of it. The AWS Organizations guide covers the rest of the service.
When does a group of accounts earn its own OU?
Create an OU when a group of accounts needs a different set of policies or a different baseline deployed to it. AWS's best practices for OUs put it plainly: OUs "should be based on function or common set of controls instead of mirroring your company's reporting structure."
A new team that needs the same SCPs as an existing one gets accounts in that OU. A different manager or cost report doesn't justify one either; cost reports already filter by account and tag.
| Signal | Create an OU? | What it changes |
|---|---|---|
| Accounts need stricter SCPs than their siblings (production, regulated data) | Yes | One attachment covers every account in the OU, including future ones |
| Accounts need a StackSet baseline the others don't | Yes | Service-managed StackSets target OUs, and with automatic deployment on, accounts added later get the baseline |
| A new team needs the same controls as an existing team | No | Add its accounts to the existing OU |
| An account is being retired | No, move it to Suspended | An SCP that denies everyone but your admin role locks it while you clean up |
| One account needs an exception to its OU's SCPs | No, move it to an Exceptions OU | Its account-level SCPs restate every guardrail except the exempted one |
Don't design the whole tree on day one either. The AWS Security Reference Architecture advises focusing on the foundational OUs and adding workload OUs as you need them. I agree, with one exception: separate production from everything else from the start, because moving accounts is what makes restructures expensive.
Three AWS organization structures, from 3 accounts to 50+
Here is the AWS organization chart at three sizes. Each OU added to the OU structure carries the policy that justified it. Which accounts you need at each stage is covered in the multi-account strategy by growth stage.
Under 10 accounts: four top-level OUs
Four top-level OUs cover a young organization:
- Security holds the log archive and security tooling accounts, protected by stricter SCPs.
- Infrastructure holds shared networking and IT services. It can wait until you share something.
- Workloads holds Prod and SDLC (development, test, staging) child OUs. AWS recommends this nesting "to isolate and tightly control production workloads."
- Suspended locks retiring accounts with an SCP that exempts only your admin role, so cleanup still works.
Create Prod and SDLC now, even with two workload accounts. Splitting production out later means moving every account in it. Flat top-level prod and dev OUs work too, as long as production gets its own.
10 to 50 accounts: add Sandbox and Policy Staging
Two OUs earn a place once several teams ship independently. A Sandbox OU lets engineers experiment under looser controls than SDLC, still inside the root's Region restriction. Policy Staging is where you test a new SCP before it guards real workloads.
Nesting 2 levels deep is usually enough. Each extra level is another inherited policy set to read when a request gets denied.
50+ accounts or client-facing: add procedural OUs
Larger organizations split Workloads further and add OUs that manage process:
- Child OUs under Workloads per business unit or client group, but only when their policies differ significantly, as the advanced OU guidance puts it.
- Exceptions, for accounts that need account-level SCPs. Moving there drops the old OU's guardrails.
- Transitional, a holding area for accounts joining from another organization before they get your full baseline.
- Deployments, if CI/CD pipelines run in dedicated accounts.
Limits to design within
These quotas come from the Organizations quotas page:
| Limit | Value | Design consequence |
|---|---|---|
| OU depth | 5 levels below the root | Keep workloads 2 levels deep for headroom |
| OUs per organization | 2,000 (the console help panel still says 1,000) | An OU per client is possible, but each needs its own reason |
| SCPs directly attached per root, OU or account | 10, each up to 10,240 characters | Consolidate related denies; FullAWSAccess takes a slot while attached |
| Accounts per organization | 10 by default, adjustable from the management account | Request the increase before onboarding a batch of clients |
Older guides say five SCPs per OU; AWS raised the limit to 10 in May 2026. Inherited SCPs don't count against a node's 10, so attach shared guardrails high in the tree.
How SCPs flow down an OU tree
An account can do only what every level above it allows, and a deny at any level wins. The SCP evaluation rules require "an explicit Allow statement at every level from the root through each OU in the direct path to the account (including the target account itself)."
That allow usually comes from FullAWSAccess, an AWS managed SCP that Organizations attaches to every root, OU and account when it's created. Remove it from an OU without a replacement and every account below loses access to everything. Your guardrails are deny statements layered on top.
Here is what a production account at Root, then Workloads, then Prod ends up with:
| Level | Attached SCPs (purpose) | Effect on the Prod account |
|---|---|---|
| Root | FullAWSAccess, deny Regions you don't use, deny leaving the organization | Only approved Regions work; can't leave the organization |
| Workloads OU | FullAWSAccess, deny creating IAM users and access keys | No long-lived IAM user credentials |
| Prod OU | FullAWSAccess, deny stopping, changing or deleting CloudTrail trails | The audit trail stays on |
| Account | FullAWSAccess | Nothing extra; all four denies apply |
An SDLC account next door inherits the root and Workloads denies but not the Prod one, which is the entire reason Prod has its own OU.
Three habits keep this manageable:
- Attach SCPs to OUs, not accounts. AWS's Cloud Operations team recommends it for easier management and troubleshooting. The Exceptions OU is the deliberate exception.
- Never attach an untested SCP to the root. AWS recommends testing on an OU first, moving accounts in one at a time. Policy Staging is that OU.
- Know what SCPs skip. They don't affect the management account or service-linked roles. They do restrict the root user of every member account.
Resource control policies (RCPs) follow the same model, while tag and backup policies merge differently (how SCP evaluation works). Copy each level's policy bodies from the SCP examples organized by OU.
Where do clients, business units and environments go?
Apply the same test to each group: does it need different controls?
- Environments go in Prod and SDLC child OUs under Workloads, because production needs tighter controls.
- Business units get a child OU only when their policies differ significantly. Otherwise they share the environment OUs, and the account name shows which unit owns it.
- Clients with identical controls share one OU. A client that needs its own controls, such as a Region restriction for data residency or a client-specific baseline, gets a child OU under Workloads.
Whether client accounts belong in your organization at all is a separate call. AWS recommends a single organization and calls it a security boundary, and your management account pays the charges of every member account, clients included.
My judgment, not AWS guidance: if the client will own the account and pay its bill, give them their own organization from the start. Leaving yours later requires contact information, a valid payment method, a verified phone number and a selected support plan on the account, plus the steps to add or remove accounts. If you run the workload as your own service, keep it under a client OU.
Name accounts and OUs so the tree reads itself
- OUs get short function names such as
workloads,prod,sdlcandclient-acme. - Accounts follow
[company]-[environment]-[function], such asacme-prod-payments, so the environment survives in a flat list. - Root emails must be unique across all AWS accounts, so a plus-address pattern like
aws+prod-payments@example.comscales well.
To fix a bad OU name, UpdateOrganizationalUnit renames it in place, keeping its ID, ARN and attached policies.
How to restructure an existing AWS organization safely
Every account move changes what that account may do. Delegated administrators for Organizations can manage policies, but they can't create OUs or move accounts, so plan this from the management account. Work through it one account at a time:
- Record which accounts sit in which OU today. For a large organization, this script that lists accounts by OU name saves the clicking.
- Create the new OUs and attach their policies. Test new SCPs in Policy Staging or on a single account before they guard a whole OU.
- Check path-based conditions. Policies using
aws:PrincipalOrgPathsoraws:ResourceOrgPathsmatch on where an account sits in the tree. Moves can add overhead for these conditions, the Security Reference Architecture warns, so find and test them first. - Move one account, then let StackSets finish before the next move.
The move itself is one call, which prints nothing on success (move-account reference):
aws organizations move-account \
--account-id 111122223333 \
--source-parent-id r-a1b2 \
--destination-parent-id ou-a1b2-f6g7h111
From then on the account inherits the new OU's policies instead of the old one's. With service-managed StackSets automatic deployment, a stack set targeting the old OU deletes its stack instance from the account, and one targeting the new OU queues a create. Move the account out and back before that finishes and it can end up with no stack instance at all. Set RetainStacksOnAccountRemoval if those resources must survive the move.
If AWS Control Tower governs your organization, create OUs in Control Tower instead, because OUs created in Organizations aren't registered, and without automatic account enrollment an enrolled account moved through Organizations lands in an inconsistent governance state (Control Tower vs AWS Organizations).
When an old OU is empty, delete it. DeleteOrganizationalUnit refuses until you've removed all of its accounts and child OUs.
Define your AWS organizational units in code
Define the tree in code so each change gets a review. CloudFormation's AWS::Organizations::OrganizationalUnit and the CDK L1 construct CfnOrganizationalUnit create OUs from the management account:
import { CfnOrganizationalUnit } from 'aws-cdk-lib/aws-organizations';
// Inside a stack deployed to the management account
const workloads = new CfnOrganizationalUnit(this, 'Workloads', {
name: 'workloads',
parentId: 'r-a1b2', // your organization root ID
});
new CfnOrganizationalUnit(this, 'WorkloadsProd', {
name: 'prod',
parentId: workloads.attrId,
});
The AWS CDK Landing Zone defines the whole tree this way, in one file: src/config/organization-structure.ts. Every OU, account and policy attachment (SCP, declarative or tag policy) lives there.
The starter ships seven OUs: security-ou, finops-ou, infrastructure-ou, workload-dev-ou, workload-prod-ou and suspended-ou, which nests a close-account-ou. Adding an OU is a new key in that file. The pull request shows the cdk diff, one new AWS::Organizations::OrganizationalUnit plus its ID output, before anything deploys. Moving a declared account means moving its entry, and synthesis checks the policies you configure on each node against the 10-per-type quota. It suits teams already working in AWS CDK and TypeScript.
What to decide before your next account
Your AWS organizational unit tree is a policy map, so draw it first:
- Group accounts by the controls they need, not by team.
- Start with Security, Infrastructure, Workloads (Prod and SDLC) and Suspended, and add an OU only when the test says so.
- Attach SCPs at OUs, test them in Policy Staging, and plan with 10 per node.
- Move accounts one at a time and let StackSets finish between moves.
Next, sketch your tree against the test table and pick each OU's guardrails from the SCP examples. For how the tree fits a full multi-account foundation, read what a landing zone sets up.
Next step