Where this fits
This is part 3 of AWS Landing Zone & Control Tower. Part 1 established the multi-account strategy and the management account; part 2 stood up the landing zone with AWS Control Tower, the Security and Log Archive accounts, and the baseline log/audit plumbing. This part decides the shape of the organization: the organizational unit (OU) hierarchy that Service Control Policies (SCPs), the AWS Control Tower controls (formerly “guardrails”), and centralized config hang off; the per-account baseline that every account inherits the moment it is vended; and the environment separation model that keeps dev, test, and prod in different blast-radius zones. OUs and account baselines are the load-bearing wall — Control Tower controls, Config conformance packs, IAM Identity Center permission sets, and your billing rollups all attach to this tree. Get the tree wrong and every later workstream inherits the debt; get it right and governance “snaps on” at the correct altitude with almost no per-account toil.

The recommended OU structure
What an OU actually is
An organizational unit in AWS Organizations is a container inside the organization root that groups AWS accounts so you can apply policy to many accounts at once through inheritance. The policy types that flow down an OU branch are Service Control Policies (the IAM permission ceiling — they never grant, only constrain the maximum available permissions), Resource Control Policies (RCPs) (a maximum-permission ceiling on resources, e.g. forcing aws:SecureTransport on every S3 bucket and SQS queue org-wide), Tag Policies, Backup Policies, and Declarative Policies for EC2/EBS/VPC. AWS Control Tower additionally attaches its controls at the OU level — preventive controls are implemented as SCPs, detective controls as AWS Config rules, and proactive controls as AWS CloudFormation Hooks.
Three hard rules shape every design decision:
- You can nest OUs up to five levels deep below the root, but depth is what bites you — every level multiplies the SCP evaluation you must reason about, and Control Tower’s own enrollment and drift detection get harder to follow.
- An account lives in exactly one OU at a time. Moving it re-evaluates the entire inherited policy set instantly, which is the mechanism behind the “Suspended” pattern below.
- SCPs are deny-biased and intersect down the branch. A child OU can only add restriction; it can never weaken a parent’s
Deny. An explicitDenyanywhere on the path wins. This is exactly why you group accounts by the guardrails they should share, not by org chart or geography.
The single most important framing: an OU is a unit of policy, not a unit of ownership. The mistake juniors make is modeling the company’s departments as OUs. The mistake architects make is modeling blast radius and shared controls as OUs and letting tags, not the tree, carry ownership.
The reference hierarchy (AWS SRA / Control Tower aligned)
The AWS Security Reference Architecture (SRA) and the Control Tower defaults converge on an opinionated, flat-ish, function-first tree. Under the organization root you create:
Root (AWS Organizations management account governs this)
├── Security (Control Tower mandatory; Audit + Log Archive live here)
│ ├── [Audit account] (cross-account security read/respond; delegated admin)
│ └── [Log Archive account](immutable CloudTrail + Config S3, central log bucket)
├── Infrastructure (shared platform services every workload consumes)
│ ├── [Network account] (Transit Gateway, central VPCs, Route 53 Resolver, NFW)
│ └── [Shared Services] (CI/CD, AMI/golden images, central ECR, IPAM, directory)
├── Workloads (where application teams' accounts live)
│ ├── Prod (production accounts, tightest controls)
│ ├── Test (pre-prod / staging accounts)
│ └── Dev (development / SDLC accounts)
├── Sandbox (loose guardrails, detached from the network, hard $ caps)
├── Policy Staging (an empty OU to safely test new SCPs/controls before rollout)
└── Suspended (quarantine: cancelled or compromised accounts, deny-all)
Why each top-level branch exists, and the representative guardrails that land there:
| OU | Purpose | Representative controls / SCPs assigned here |
|---|---|---|
| Security | Centralized security tooling and the immutable audit trail. Control Tower requires this OU and puts the Audit and Log Archive accounts in it. | Protect CloudTrail/Config from disablement; deny deletion of the log bucket; deny leaving the org; enforce GuardDuty/Security Hub delegated admin |
| Infrastructure | Shared platform plane the whole org consumes — networking, CI/CD, golden AMIs, central ECR/IPAM/DNS. Tighter change control than workloads. | Region deny; deny public S3; restrict who can modify Transit Gateway/RAM shares; require IMDSv2; protect shared route tables |
| Workloads | The home for application accounts, sub-divided by environment (Prod/Test/Dev). The bulk of accounts live here. | Baseline app guardrails: deny root usage, require encryption-at-rest, deny unencrypted EBS, region allow-list, mandatory tags |
| Workloads/Prod | Production accounts — the tightest, most stable control set; changes go through change control. | Deny *:Delete* on logging/backup; deny RDS/EBS without encryption; deny IAM user creation; stricter region list; deny disabling backup vault lock |
| Workloads/Test | Staging / pre-prod, close to prod but allows some break-glass for validation. | Prod controls minus a few break-glass denies; smaller instance-family allow-list for cost |
| Workloads/Dev | Developer SDLC accounts; productive but never internet-trusted with prod data. | Deny production data access; budget actions; restrict expensive instance families; deny public RDS |
| Sandbox | Free experimentation disconnected from the corporate network, with hard spend caps and no access to prod data. | Network isolation (deny VPC peering/TGW attach to corp), deny on data exfil paths, AWS Budgets actions, region lock |
| Policy Staging | A deliberately near-empty OU used to test a new SCP or Control Tower control on a throwaway account before promoting it to Workloads/Prod. | Whatever policy is currently under test — this OU’s contents change constantly by design |
| Suspended (a.k.a. Quarantine) | Quarantine pen for cancelled accounts awaiting closure and for compromised accounts moved here during incident response. | A single deny-all SCP (Deny *) with a tiny break-glass allow for forensics; detaches all other inherited permissions instantly |
Security OU
This is the only OU AWS Control Tower mandates and pre-populates: when you set up the landing zone it creates the Security OU and provisions the Audit and Log Archive accounts into it. The Audit account is your cross-account security-operations seat — it holds the read/respond roles and is the natural delegated administrator for GuardDuty, Security Hub, Macie, Detective, IAM Access Analyzer, and Inspector, so security tooling runs out of the management account (a hard AWS best practice). The Log Archive account holds the central S3 bucket that receives organization CloudTrail and Config snapshots, ideally with S3 Object Lock (WORM) and a bucket policy that even the org admin cannot weaken without leaving a trace.
The SCPs here are the “never touch the evidence” set: deny cloudtrail:StopLogging, cloudtrail:DeleteTrail, config:DeleteConfigurationRecorder, config:StopConfigurationRecorder, and any s3:DeleteBucket/s3:PutBucketPolicy against the log bucket. You also deny organizations:LeaveOrganization here so a security account can never be detached from governance.
Infrastructure OU
The Infrastructure OU holds the shared platform plane — the things every workload account consumes but should never own. In a hub-and-spoke landing zone the Network account owns the Transit Gateway, the centralized inspection/egress VPC (often with AWS Network Firewall), Route 53 Resolver rules and inbound/outbound endpoints, and the RAM (Resource Access Manager) shares that hand subnets out to workload accounts via VPC sharing. A separate Shared Services account owns the CI/CD tooling, the central Amazon ECR registries, golden AMI/EC2 Image Builder pipelines, VPC IPAM pools, and any AWS Directory Service / AD Connector.
Why split networking out at all? Because the Transit Gateway and central route tables are the component whose misconfiguration takes down every connected account at once — it deserves its own account, its own change control, and its own tighter SCP set (deny modifying TGW route tables outside a pipeline role, deny RAM share deletion, require IMDSv2, region-lock).
Workloads OU and its environment sub-OUs
This is where the actual applications live, and it is the only branch you sub-divide by environment. The recommended pattern is Workloads → {Prod, Test, Dev}, with each application getting separate accounts per environment (more on why under “Environment separation”). Some large orgs add a second axis — e.g. Workloads/Prod/Regulated vs Workloads/Prod/Standard, or Workloads/Prod/SOX — to layer compliance-specific controls (PCI, HIPAA, SOX) on top of the environment baseline. Resist going deeper than that: two meaningful axes (environment × compliance class) is almost always enough, and each extra level is an extra SCP intersection to reason about.
Sandbox OU
The Sandbox OU exists so engineers can experiment without punching holes in production guardrails and without touching the corporate network. The defining characteristic is isolation: SCPs deny creating VPC peering/TGW attachments to corp networks, deny actions that could move prod data in, and AWS Budgets actions apply a hard spend cap that can auto-attach a restrictive policy when a threshold is breached. Sandbox accounts are deliberately cattle, not pets — vended quickly, recycled aggressively, and never trusted with anything that matters.
Policy Staging OU
The Policy Staging OU is the one most teams forget, and it is what separates a mature platform from a fragile one. Because an SCP Deny is absolute and applies instantly to every account in the branch, a bad SCP can lock out an entire production OU in one click. The Policy Staging OU is a near-empty OU containing one or two throwaway accounts that mirror the access patterns of a real workload account. You attach the candidate SCP or new Control Tower control here first, run a smoke test (deploy a representative stack, assume the app role, exercise the deny paths), confirm nothing legitimate breaks, then promote the policy to Workloads/Prod. It turns “we changed an SCP and took down prod” into a non-event.
Suspended OU
The Suspended (or Quarantine) OU is a two-job container. Job one: cancelled accounts awaiting the 90-day closure window sit here, locked down so nobody spins new resources up in them. Job two — the one that earns its keep — is incident response: when an account is compromised, the fastest containment is to move it into Suspended, where a single Deny * SCP (with a narrow break-glass allow for forensics roles) strips every inherited permission instantly without you having to unwind individual IAM changes the attacker may have made. Because OU membership re-evaluates policy immediately, this is the cleanest “big red button” AWS Organizations gives you.
Why not model by org chart, region, or business unit?
| Tempting model | Why it fails | What to do instead |
|---|---|---|
| One OU per business unit / team | Teams reorg constantly; SCPs don’t map to teams; you get dozens of shallow OUs with near-identical controls | Group by shared guardrails (env, compliance); carry ownership in tags + a separate AWS account |
| One OU per AWS Region | Regions are a control concern, not a grouping concern — solve with a region-deny SCP, not topology | Use a Region allow-list SCP at the root/Infrastructure level; let accounts be multi-region |
| Deep nesting to mirror the company hierarchy | Five-level limit; every level is another SCP intersection; Control Tower drift gets unreadable | Stay flat: two meaningful axes (environment × compliance) is the ceiling for almost everyone |
| One giant OU for “everything non-prod” | Loses the dev-vs-test distinction you need for promotion gates and cost controls | Split Dev and Test even if controls are similar today — you will want the seam later |
Per-account baselines
What a baseline is
A per-account baseline is the set of resources, configuration, and guardrails that every account receives automatically at vend time and continuously thereafter, so that no account is ever “naked.” There are two complementary layers:
- The Control Tower baseline applied at enrollment — what AWS sets up the moment the account joins an enrolled OU.
- Your custom baseline — the org-specific resources and config you bolt on via Account Factory customizations, blueprints, or a post-provisioning pipeline (the Account Factory for Terraform / AFT pattern, or Customizations for Control Tower / CfCT using CloudFormation StackSets).
What Control Tower bakes in automatically
When an account is provisioned through Account Factory (Control Tower’s account-vending mechanism, surfaced through AWS Service Catalog) and enrolled into a governed OU, it inherits:
| Baseline element | What it does | Mechanism |
|---|---|---|
| Org CloudTrail delivery | All management events land in the central Log Archive bucket | Organization trail managed by Control Tower |
| AWS Config recorder + delivery | Config records resource state and ships snapshots to Log Archive | Config recorder + aggregator |
| IAM Identity Center access | The account is reachable via SSO permission sets, not long-lived IAM users | IAM Identity Center (formerly AWS SSO) |
| Mandatory preventive controls | Core “you can’t disable the audit plane” SCPs | SCPs attached at the OU |
| Detective controls | Config rules flag drift (e.g. public S3, unencrypted volumes) | AWS Config managed/custom rules |
| Baseline IAM roles | The Control Tower execution role + audit cross-account roles | CloudFormation StackSet (AWSControlTowerExecution) |
| Default encryption / log config | KMS key wiring for the trail/Config where configured | Control Tower managed resources |
Crucially, you never create these by hand per account — they ride along with enrollment. The architect’s job is to (a) ensure new accounts are always vended into a governed OU, and (b) define the custom layer below.
The custom baseline you add
Beyond what Control Tower gives you, almost every enterprise layers on a standard set. The decision is how to deliver it consistently:
| Custom baseline item | Why every account needs it | Typical delivery |
|---|---|---|
| VPC topology (or no VPC) | A right-sized VPC sharing the corp CIDR via RAM, or explicitly none for serverless-only accounts | AFT/CfCT StackSet; delete the default VPC in every Region |
| GuardDuty / Security Hub / Inspector / Macie enrollment | Threat detection and posture must be on from minute zero | Org-level auto-enable via delegated admin (Audit account) |
| EBS encryption-by-default + KMS | No unencrypted volume should ever exist | ec2:EnableEbsEncryptionByDefault set per Region via StackSet |
| S3 Block Public Access (account-level) | Closes the most common data-leak vector at the account boundary | Account-level BPA set on baseline |
| IMDSv2 enforcement | Mitigates SSRF credential theft | Declarative Policy / SCP + launch templates |
| Default IAM roles & break-glass | A read-only auditor role, a break-glass admin role, the CI/CD deploy role | Identity Center permission sets + StackSet roles |
| Budgets + cost anomaly detection | Every account must have a spend ceiling and anomaly alerts | AWS Budgets + Cost Anomaly Detection via StackSet |
| Centralized logging wiring | VPC Flow Logs, CloudWatch → central, DNS query logging | StackSet to the central log destination |
| Tagging at birth | Environment, CostCenter, Owner, DataClassification enforced by Tag Policy |
Account Factory parameters + Tag Policy |
Account Factory, AFT, and Customizations for Control Tower
There are three account-vending postures, and choosing among them is a real architectural decision:
| Approach | What it is | When to choose it |
|---|---|---|
| Account Factory (console / Service Catalog) | Click-ops or Service Catalog product to vend a single account with basic parameters | Small orgs; <~30 accounts; low change rate |
| Account Factory for Terraform (AFT) | A Terraform pipeline (GitOps) that vends accounts, runs global and account-specific customizations, and re-applies on every account | Terraform shops; you want account requests as pull requests and drift-corrected baselines |
| Customizations for Control Tower (CfCT) | A CloudFormation/StackSet pipeline triggered by lifecycle events to apply CFN templates to OUs/accounts | CloudFormation shops; you want to layer StackSets onto Control Tower’s lifecycle |
The non-negotiable principle across all three: the baseline is code, applied idempotently, and re-converged. Drift is detected (Config + Control Tower drift detection) and corrected (re-run AFT / re-deploy the StackSet), so an account that someone hand-edits snaps back to baseline. A baseline that exists only as a runbook is not a baseline.
Account-vending request as an artifact
The concrete artifact of this discipline is the account request itself — in AFT this is a small Terraform module instance (or in CfCT, an entry in a manifest) capturing: account name and email (a unique, deliverable address, usually a +-suffixed alias on a shared mailbox), the target OU, the owner/cost-center/environment tags, the SSO access (which permission sets, which groups), and any account-specific customization toggles (e.g. “this account gets a shared VPC subnet,” “this is a serverless-only account, no VPC”). That request is reviewed via pull request, which gives you an auditable history of why every account exists.
Environment separation
Why separate accounts per environment, not VPCs or tags
The single strongest isolation boundary AWS gives you is the account. Service quotas, IAM trust, billing, and — critically — blast radius all stop at the account line in a way they never do at a VPC or tag boundary. The principle the AWS multi-account strategy hammers is: isolate production workloads in their own accounts, and give each application its own set of accounts per environment. So a single app “Orders” gets orders-dev, orders-test, and orders-prod as three separate accounts, sitting in Workloads/Dev, Workloads/Test, and Workloads/Prod respectively.
| Isolation boundary | Strength | What still leaks across it |
|---|---|---|
Tag (one account, env=prod/dev) |
Weakest | IAM blast radius, service quotas, a fat-fingered * policy, a runaway bill |
| VPC (one account, multiple VPCs) | Weak–medium | IAM and the account-wide control plane; quota exhaustion; the root user |
| Account per environment | Strongest | Almost nothing — by design; cross-account is explicit and auditable |
Separate accounts also make the promotion story clean: a CI/CD pipeline in Shared Services assumes a deploy role into orders-dev, then orders-test, then orders-prod, with a manual approval gate before prod. Each environment can carry a different SCP set purely by living in a different OU — dev can be allowed to spin up cheap experiments that prod denies, and prod can deny the IAM-user creation and backup-vault-tampering that dev tolerates.
How environment separation maps onto the OU tree
Environment is expressed twice, on purpose, and both must agree:
- Structurally, via OU placement: the account sits in
Workloads/Prodvs/Testvs/Dev, which controls which SCPs and Control Tower controls it inherits. - Descriptively, via a mandatory
Environmenttag enforced by a Tag Policy, which controls cost allocation, automation behavior, and queryability.
The two must never disagree — an account tagged prod sitting in the Dev OU is a governance bug. A periodic Config rule (or an AFT post-check) that flags “Environment tag != OU” catches exactly this drift.
The promotion and connectivity model
Environment separation is not just about blocking — it is about controlled flow:
- Network: prod, test, and dev attach to different Transit Gateway route domains (or separate TGWs) so a dev box can never route to a prod database. Sandbox attaches to nothing corporate.
- Data: prod data classification (e.g.
Confidential) is denied from being copied into dev by SCP/RCP on the data-store actions; dev uses synthetic or masked data. - Identity: different Identity Center permission sets per OU — broad
Developeraccess in Dev, tightly scopedReadOnly+ break-glass in Prod, with privileged prod access fronted by approval (e.g. a temporary-elevation workflow). - Deploy: one pipeline, three stages, assuming environment-specific deploy roles, with the prod stage gated by a human approval and (often) a change-management ticket reference.
Real-world enterprise scenario
Context. Meridian Cargo, a fictional mid-size logistics SaaS, runs a fleet-tracking and freight-booking platform. They have 9 product teams, roughly 60 microservices, a hard PCI-DSS obligation (they store cardholder data for freight payments), and a board mandate to “move to a governed multi-account model in one quarter.” They start from a sprawling two-account mess (one “everything-dev,” one “everything-prod”) with shared IAM, no central logging, and a $90k/month bill nobody can attribute.
The decisions, per sub-component.
OU structure. Their platform team stands up AWS Control Tower, accepting the mandated Security OU (Audit + Log Archive). They then create Infrastructure (Network + Shared Services accounts), Workloads with Prod / Test / Dev children, a Sandbox OU, a Policy Staging OU, and a Suspended OU. Because PCI is in scope, they split Workloads/Prod into Prod/PCI and Prod/Standard — a second axis — so the PCI accounts inherit an extra conformance pack and a tighter region allow-list (us-east-1, us-west-2 only) on top of the prod baseline. Final tree: 7 top-level OUs, one two-level split under Prod — never deeper. They explicitly reject the team-per-OU model their first draft had (it produced 9 near-identical OUs); ownership moves to a mandatory Owner tag plus the account itself.
Per-account baselines. They adopt Account Factory for Terraform (AFT) because they are already a Terraform shop. Every account request becomes a pull request. The global customization layer (applied to all accounts) deletes the default VPC in every Region, sets EBS encryption-by-default, turns on account-level S3 Block Public Access, enforces IMDSv2 via a Declarative Policy, wires VPC Flow Logs + CloudTrail to the central Log Archive bucket, auto-enables GuardDuty, Security Hub, Inspector, and Macie through the Audit account as delegated admin, and stamps an AWS Budgets ceiling with Cost Anomaly Detection. The account-specific layer attaches a shared-VPC subnet (via RAM) for accounts that need one, or no VPC at all for the 12 serverless-only accounts. PCI accounts additionally receive an AWS Config conformance pack mapped to the PCI-DSS controls and a backup plan with AWS Backup Vault Lock enabled.
Environment separation. Each of the 9 product teams gets three accounts per product (dev/test/prod), so “freight-booking” becomes freight-dev, freight-test, freight-prod. They end up with ~60 workload accounts plus the 4 foundational ones (management, audit, log-archive, network) and shared-services — about 66 accounts total. Dev attaches to a non-prod TGW route domain; prod to a prod-only domain; the two cannot route to each other. A single AWS CodePipeline in Shared Services promotes builds dev → test → prod, assuming an environment-specific deploy role at each stage, with a manual approval and a change ticket required before the prod stage. A Config rule flags any account whose Environment tag disagrees with its OU.
The artifacts they produce. An OU design diagram and decision record; an AFT repository with the global + per-account customization modules; ~66 account-request Terraform module instances under version control; the SCP set per OU (with the candidate-SCP workflow routed through Policy Staging); the PCI conformance pack and Tag Policy; and an Identity Center permission-set matrix mapping AD groups → permission sets → OUs.
The measurable outcome. Within the quarter: 100% of accounts enrolled in a governed OU with the baseline applied automatically; central CloudTrail/Config with zero accounts able to disable the audit plane (verified by attempting it from Policy Staging); PCI scope reduced to the 9 Prod/PCI accounts only, shrinking the audit surface; cost now attributable to a team and environment for every dollar (cost-allocation tags enforced by Tag Policy); and a live drill where a deliberately “compromised” test account was contained in under two minutes by moving it to the Suspended OU — the Deny * SCP stripped every permission instantly.
Deliverables & checklist
By the end of this phase you should have produced:
Common pitfalls
- Modeling OUs on the org chart. Departments reorg; SCPs don’t follow people. You end up with many shallow, near-identical OUs and constant churn. Fix: group by shared guardrails (environment × compliance), carry ownership in tags and the account boundary.
- No Policy Staging OU. Teams attach a new SCP straight to
Workloads/Prod, aDenyis broader than intended, and an entire production OU is locked out in one click. Fix: always promote candidate SCPs/controls through a Policy Staging OU against a mirror account first. - Hand-built, un-reconverged baselines. Someone documents the baseline in a runbook and applies it manually, so accounts drift and “naked” accounts appear. Fix: deliver the baseline as code (AFT/CfCT StackSets), detect drift with Config + Control Tower, and re-converge automatically.
- One account with
envtags instead of accounts per environment. Tags don’t stop an IAM blast radius, a runaway bill, or quota exhaustion crossing from dev into prod. Fix: separate accounts per environment, placed in the matching Workloads sub-OU. - Over-nesting the tree. Five-level-deep hierarchies mirroring the company make SCP intersection and Control Tower drift unreadable. Fix: stay flat — two meaningful axes is the ceiling for almost everyone.
- Running security tooling from the management account. Centralizing GuardDuty/Security Hub/Config aggregation in the management account violates AWS guidance and widens its blast radius. Fix: delegate administration to the Audit account in the Security OU.
- Vending accounts outside a governed OU. An account created directly in the root (or an un-enrolled OU) inherits no baseline and becomes a shadow-IT liability. Fix: force all vending through the pipeline into an enrolled OU, and alert on any account landing in Root.
What’s next
Part 4 of AWS Landing Zone & Control Tower moves from the account/OU skeleton to the policy plane itself — authoring and operating Service Control Policies, Resource Control Policies, and Control Tower controls as code, including the proactive/preventive/detective control mix and how to manage them at scale.