AWS Organizations in Production: Structure, SCP Evaluation, and Landing Zone Gotchas
We need to reformat the article into clean Markdown, preserving all facts, names, numbers, quotes. No extra commentary. Use headings where topic shifts. Use bullet or numbered lists for enumerations. Use inline code or fenced code blocks for code/commands/identifiers.
We have a long article with repeated sections (some duplication). We need to produce a single article body, not repeating title. Should we keep the duplicated content? The raw article appears to have duplicated paragraphs (the first part repeated). We must preserve every fact, name, number, and quotation exactly as given. That means we cannot remove duplicated content? The instruction: "Preserve every fact, name, number, and quotation exactly as given. Never invent, add, remove, or reorder information." If the source contains duplicated text, we must keep it as is? However, we are allowed to restructure and lightly polish wording for flow and grammar; but we cannot remove information. Duplicated text is still information; removing it would be removing information. So we must keep the duplicated content. However, we can restructure: we could keep the duplicated sections as they appear, maybe as separate paragraphs. But we can also keep them as is; we just need to format.
Thus we need to output the article body as markdown, with headings where topic shifts. The article already has headings like "The Account Is the Boundary", "Structure", etc. We can turn those into ## headings.
We need to break into short paragraphs separated by blank line.
We need to use bullet or numbered lists for enumerations. Already there are bullet lists with dash. We can keep them.
We need to use inline code or fenced code blocks where code, commands, or identifiers appear. For example: aws organizations list-roots, etc. Also identifiers like FullAWSAccess, etc. We'll wrap those in backticks.
We need to preserve quotes exactly. There are quotes like "why is this denied?" etc.
We need to not wrap whole answer in a code fence. Not repeat article title as heading.
We need to output only reformatted article body.
Let's parse the raw article and produce markdown.
Raw article (including duplication) is:
Title: AWS Organizations in Production: Structure, SCP Evaluation, and Landing Zone Gotchas
Article:
Most teams adopt AWS Organizations for consolidated billing and stop there. The real value is the control plane: a tree of accounts where guardrails attached at the top apply to everything below. That same tree is also where most "why is this denied?" tickets come from, because the evaluation rules are less intuitive than IAM alone. This post covers the structure, how policies are evaluated, and the gotchas worth knowing before you run it in production. The Account Is the Boundary An AWS account is the hard boundary for resources, security, and billing. IAM manages identities inside an account. Organizations manages the accounts themselves. That distinction drives the whole multi-account model: separate accounts give you blast-radius isolation, clean cost attribution, and a place to attach controls that no one inside the account can override. Structure An organization has: One management account. It owns the organization and is the payer account. It should run org and billing tasks only. No workloads. One root. The top of the tree. Policies attached here apply to every account. Organizational units (OUs). Containers for accounts. OUs can nest up to five levels deep under the root. Member accounts. Where workloads, logging, and security tooling live. Policies. Attached at the root, an OU, or an individual account. A common baseline layout: OU Accounts Purpose Security Log Archive, Security Tooling (Audit) Centralized logs, delegated admin for security services Infrastructure Network, Shared Services Transit, DNS, shared tooling Workloads Prod, NonProd Application accounts, grouped by lifecycle Sandbox Per-developer or per-team Experimentation with looser controls Suspended Accounts pending closure Deny-all quarantine Tip: Design OUs around the controls and lifecycle you want to apply, not around the org chart. Teams get reorganized; "prod needs stricter guardrails than sandbox" does not change. Enable All Features Organizations has two feature sets. Consolidated-billing mode only merges invoices. All features adds the authorization policies (SCPs, RCPs), management policies (tag, backup, EC2), trusted access for service integrations, and delegated administrators. If you are building a landing zone, you need all features. Policy Types That Matter Day to Day Policy What it does SCP (service control policy) Sets the maximum permissions for IAM users and roles in member accounts. Grants nothing on its own. RCP (resource control policy) Sets the maximum permissions on resources in member accounts (S3, KMS, Secrets Manager, SQS, and many more), regardless of who is calling. Grants nothing on its own. Tag policy Standardizes tag keys and values so cost allocation and ABAC stay consistent. Backup policy Deploys AWS Backup plans centrally across accounts and Regions. EC2 policy (declarative) Enforces EC2, VPC, and EBS baselines org-wide, such as IMDS defaults, AMI and EBS snapshot block public access, serial console access, and VPC Block Public Access. The configuration is maintained even as the service adds new APIs. How Inheritance Actually Works This is the part that trips people up. A Deny is simple: attach it anywhere and it applies to everything below that point. An Allow is not inherited the way people expect. For an action to be permitted in an account, an SCP must allow it at every level from the root down to the account: the root, each OU in the path, and the account itself. If any level is missing the Allow, the action is implicitly denied, even if an IAM policy in the account grants : . RootFullAWSAccess-> allows * OU WorkloadsAllowOnlyApprovedSvc -> allows ec2:, s3:, ... (no dynamodb:) Prod acct FullAWSAccess-> allows * Result in Prod: dynamodb: is denied. The OU level never allowed it. This is why the default FullAWSAccess SCP exists at every level. If you replace it with an allow-list at the OU, that allow-list becomes the ceiling for every account underneath. Most common SCP incident: Someone detaches FullAWSAccess from an OU while testing an allow-list SCP, and every account under that OU loses access to services that weren't on the list. Prefer deny-list SCPs (explicit Deny statements on top of FullAWSAccess ) unless you have a specific reason to allow-list. How a Request Is Evaluated For a principal in a member account, access requires an Allow from every applicable layer: Identity-based policy on the user or role must allow the action. SCPs in the account's path must allow it. RCPs in the path must allow it for the target resource (if the service supports RCPs). Permission boundaries, session policies, and resource-based policies still apply as usual. An explicit Deny at any layer wins. SCPs and RCPs only ever reduce what is possible; they never grant access. A practical consequence: when you debug an AccessDenied in a member account and the IAM policy looks correct, check the SCPs on every node in the account's path, not just the ones attached directly to the account. Landing Zone Pattern The goal is to separate the governance plane from workloads: Management account: org and billing only. Tightly restricted human access. Security OU: Log Archive receives org-wide CloudTrail and Config data. Security Tooling (Control Tower calls it Audit) is the delegated administrator for GuardDuty, Security Hub, and similar services. Infrastructure OU: networking and shared services. Workloads OU: prod and non-prod split so guardrails can differ. AWS Control Tower orchestrates this on top of Organizations: Account Factory for provisioning, managed controls (preventive via SCPs/RCPs, detective via AWS Config, proactive via CloudFormation hooks), and drift detection. If you manage infrastructure as code, Account Factory for Terraform (AFT) provisions accounts through a Terraform pipeline. Operating Lifecycle Identity: IAM Identity Center for human access, cross-account roles for automation, least privilege throughout. Provisioning: Account Factory, AFT, or the Organizations API. Never click-ops accounts in production. Baseline: logging, Config, security services, and networking applied from day one, typically via StackSets or Control Tower. Guardrails: preventive, detective, and proactive controls, plus drift detection. Lifecycle: move accounts between OUs as their purpose changes. To retire one, move it to a Suspended OU with a deny-all SCP, then close it. A closed account can be reopened during the post-closure period (90 days). Production Gotchas SCPs and RCPs never restrict the management account. Anything you run there is outside your guardrails. That is the main reason to keep workloads out of it. SCPs don't affect service-linked roles. AWS services acting through service-linked roles are not limited by your SCPs. SCPs do apply to delegated administrator accounts. They are member accounts. Make sure your guardrails don't block the security services you delegated. OUs nest at most five levels deep. Deep hierarchies are also harder to reason about during an incident. Keep it shallow. Delegate security services to the Audit/Security Tooling account instead of administering them from the management account. Limits: SCP documents max out at 10,240 characters, and each root, OU, or account can have up to 10 SCPs attached. Plan statement consolidation early. Quick Inventory Commands # Root ID and enabled policy types aws organizations list-roots # All accounts in the org aws organizations list-accounts # OUs under a parent (root or OU) aws organizations list-organizational-units-for-parent --parent-id r-examplerootid # SCPs attached to a specific target aws organizations list-policies-for-target \ --target-id 123456789012 \ --filter SERVICE_CONTROL_POLICY Run these from the management account or a delegated administrator. Sources What is AWS Organizations? AWS Organizations terminology and concepts Service control policies (SCPs) Resource control policies (RCPs) EC2 policies Best practices for the management account Best practices for organizational units Quotas for AWS Organizations What is AWS Control Tower? AWS multi-account landing zone
Most teams adopt AWS Organizations for consolidated billing and stop there. The real value is the control plane: a tree of accounts where guardrails attached at the top apply to everything below. That same tree is also where most "why is this denied?" tickets come from, because the evaluation rules are less intuitive than IAM alone. This post covers the structure, how policies are evaluated, and the gotchas worth knowing before you run it in production. The Account Is the Boundary An AWS account is the hard boundary for resources, security, and billing. IAM manages identities inside an account. Organizations manages the accounts themselves. That distinction drives the whole multi-account model: separate accounts give you blast-radius isolation, clean cost attribution, and a place to attach controls that no one inside the account can override. Structure An organization has: - One management account. It owns the organization and is the payer account. It should run org and billing tasks only. No workloads. - One root. The top of the tree. Policies attached here apply to every account. - Organizational units (OUs). Containers for accounts. OUs can nest up to five levels deep under the root. - Member accounts. Where workloads, logging, and security tooling live. - Policies. Attached at the root, an OU, or an individual account. A common baseline layout: | OU | Accounts | Purpose | |---|---|---| | Security | Log Archive, Security Tooling (Audit) | Centralized logs, delegated admin for security services | | Infrastructure | Network, Shared Services | Transit, DNS, shared tooling | | Workloads | Prod, NonProd | Application accounts, grouped by lifecycle | | Sandbox | Per-developer or per-team | Experimentation with looser controls | | Suspended | Accounts pending closure | Deny-all quarantine | Tip: Design OUs around the controls and lifecycle you want to apply, not around the org chart. Teams get reorganized; "prod needs stricter guardrails than sandbox" does not change. Enable All Features Organizations has two feature sets. Consolidated-billing mode only merges invoices. All features adds the authorization policies (SCPs, RCPs), management policies (tag, backup, EC2), trusted access for service integrations, and delegated administrators. If you are building a landing zone, you need all features. Policy Types That Matter Day to Day | Policy | What it does | |---|---| | SCP (service control policy) | Sets the maximum permissions for IAM users and roles in member accounts. Grants nothing on its own. | | RCP (resource control policy) | Sets the maximum permissions on resources in member accounts (S3, KMS, Secrets Manager, SQS, and many more), regardless of who is calling. Grants nothing on its own. | | Tag policy | Standardizes tag keys and values so cost allocation and ABAC stay consistent. | | Backup policy | Deploys AWS Backup plans centrally across accounts and Regions. | | EC2 policy (declarative) | Enforces EC2, VPC, and EBS baselines org-wide, such as IMDS defaults, AMI and EBS snapshot block public access, serial console access, and VPC Block Public Access. The configuration is maintained even as the service adds new APIs. | How Inheritance Actually Works This is the part that trips people up. A Deny is simple: attach it anywhere and it applies to everything below that point. An Allow is not inherited the way people expect. For an action to be permitted in an account, an SCP must allow it at every level from the root down to the account: the root, each OU in the path, and the account itself. If any level is missing the Allow, the action is implicitly denied, even if an IAM policy in the account grants : . Root FullAWSAccess -> allows * OU Workloads AllowOnlyApprovedSvc -> allows ec2:, s3:, ... (no dynamodb:) Prod acct FullAWSAccess -> allows * Result in Prod: dynamodb: is denied. The OU level never allowed it. This is why the default FullAWSAccess SCP exists at every level. If you replace it with an allow-list at the OU, that allow-list becomes the ceiling for every account underneath. Most common SCP incident: Someone detaches FullAWSAccess from an OU while testing an allow-list SCP, and every account under that OU loses access to services that weren't on the list. Prefer deny-list SCPs (explicitDeny statements on top ofFullAWSAccess ) unless you have a specific reason to allow-list. How a Request Is Evaluated For a principal in a member account, access requires an Allow from every applicable layer: - Identity-based policy on the user or role must allow the action. - SCPs in the account's path must allow it. - RCPs in the path must allow it for the target resource (if the service supports RCPs). - Permission boundaries, session policies, and resource-based policies still apply as usual. An explicit Deny at any layer wins. SCPs and RCPs only ever reduce what is possible; they never grant access. A practical consequence: when you debug an AccessDenied in a member account and the IAM policy looks correct, check the SCPs on every node in the account's path, not just the ones attached directly to the account. Landing Zone Pattern The goal is to separate the governance plane from workloads: - Management account: org and billing only. Tightly restricted human access. - Security OU: Log Archive receives org-wide CloudTrail and Config data. Security Tooling (Control Tower calls it Audit) is the delegated administrator for GuardDuty, Security Hub, and similar services. - Infrastructure OU: networking and shared services. - Workloads OU: prod and non-prod split so guardrails can differ. AWS Control Tower orchestrates this on top of Organizations: Account Factory for provisioning, managed controls (preventive via SCPs/RCPs, detective via AWS Config, proactive via CloudFormation hooks), and drift detection. If you manage infrastructure as code, Account Factory for Terraform (AFT) provisions accounts t
We need to preserve everything exactly, including the incomplete last line "provisions accounts t". That seems cut off. We must keep it as is.
Now we need to format.
We'll create sections:
Introduction (maybe from first paragraph until "The Account Is the Boundary").
But we must not add extra info not present. However we can create headings based on existing headings in text: "The Account Is the Boundary", "Structure", "Enable All Features", "Policy Types That Matter Day to Day", "How Inheritance Actually Works", "How a Request Is Evaluated", "Landing Zone Pattern", "Operating Lifecycle", "Production Gotchas", "Quick Inventory Commands", "Sources". Also there is a duplicated section after Sources that repeats the intro again. We'll keep that as is, maybe as another section? It repeats the same content; we could treat it as a repeated block but we must keep it.
Thus after Sources, we have the duplicated intro again. We'll keep it as a paragraph.
We need to ensure we break into short paragraphs.
We'll need to convert bullet lists to markdown bullet lists.
We need to convert tables to markdown tables (they already are in markdown-like format with pipes). We'll keep them.
We need to wrap code identifiers in backticks: e.g., FullAWSAccess, AllowOnlyApprovedSvc, aws organizations list-roots, etc.
Also need to wrap commands in fenced code blocks? The instruction: "Use bullet or numbered lists for enumerations, and inline code or fenced code blocks where code, commands, or identifiers appear." So we can use inline code for identifiers and short commands; for longer commands we could use fenced code block. The Quick Inventory Commands section includes multi-line commands; we can put them in a fenced code block.
We need to preserve the exact text, including spaces and line breaks. We'll adjust formatting but keep the content.
Let's produce.
We'll start with the first paragraph:
"Most teams adopt AWS Organizations for consolidated billing and stop there. The real value is the control plane: a tree of accounts where guardrails attached at the top apply to everything below. That same tree is also where most "why is this denied?" tickets come from, because the evaluation rules are less intuitive than IAM alone. This post covers the structure, how policies are evaluated, and the gotchas worth knowing before you run it in production."
Then a blank line.
Then heading: ## The Account Is the Boundary
Then paragraph: "An AWS account is the hard boundary for resources, security, and billing. IAM manages identities inside an account. Organizations manages the accounts themselves. That distinction drives the whole multi-account model: separate accounts give you blast-radius isolation, clean cost attribution, and a place to attach controls that no one inside the account can override."
Then blank line.
Then heading: ## Structure
Then paragraph: "An organization has:" then bullet list.
We need to keep the bullet list exactly as given: "One management account. It owns the organization and is the payer account. It should run org and billing tasks only. No workloads." etc.
We'll convert to markdown bullet list with dash.
We need to preserve the text after each dash.
Let's extract:
- One management account. It owns the organization and is the payer account. It should run org and billing tasks only. No workloads.
- One root. The top of the tree. Policies attached here apply to every account.
- Organizational units (OUs). Containers for accounts. OUs can nest up to five levels deep under the root.
- Member accounts. Where workloads, logging, and security tooling live.
- Policies. Attached at the root, an OU, or an individual account.
Then after that, there is "A common baseline layout:" then a table.
We'll keep that paragraph then the table.
We need to preserve the table exactly as given, with pipes and dashes.
The table:
| OU | Accounts | Purpose |
|---|---|---|
| Security | Log Archive, Security Tooling (Audit) | Centralized logs, delegated admin for security services |
| Infrastructure | Network, Shared Services | Transit, DNS, shared tooling |
| Workloads | Prod, NonProd | Application accounts, grouped by lifecycle |
| Sandbox | Per-developer or per-team | Experimentation with looser controls |
| Suspended | Accounts pending closure | Deny-all quarantine |
We need to keep that.
Then after table: "Tip: Design OUs around the controls and lifecycle you want to apply, not around the org chart. Teams get reorganized; "prod needs stricter guardrails than sandbox" does not change."
We need to preserve the quotes inside.
Now heading: ## Enable All Features
Paragraph: "Organizations has two feature sets. Consolidated-billing mode only merges invoices. All features adds the authorization policies (SCPs, RCPs), management policies (tag, backup, EC2), trusted access for service integrations, and delegated administrators. If you are building a landing zone, you need all features."
Then heading: ## Policy Types That Matter Day to Day
Then a table:
| Policy | What it does |
|---|---|
| SCP (service control policy) | Sets the maximum permissions for IAM users and roles in member accounts. Grants nothing on its own. |
| RCP (resource control policy) | Sets the maximum permissions on resources in member accounts (S3, KMS, Secrets Manager, SQS, and many more), regardless of who is calling. Grants nothing on its own. |
| Tag policy | Standardizes tag keys and values so cost allocation and ABAC stay consistent. |
| Backup policy | Deploys AWS Backup plans centrally across accounts and Regions. |
| EC2 policy (declarative) | Enforces EC2, VPC, and EBS baselines org-wide, such as IMDS defaults, AMI and EBS snapshot block public access, serial console access, and VPC Block Public Access. The configuration is maintained even as the service adds new APIs. |
We need to keep that.
Then heading: ## How Inheritance Actually Works
Paragraph: "This is the part that trips people up. A Deny is simple: attach it anywhere and it applies to everything below that point. An Allow is not inherited the way people expect. For an action to be permitted in an account, an SCP must allow it at every level from the root down to the account: the root, each OU in the path, and the account itself. If any level is missing the Allow, the action is implicitly denied, even if an IAM policy in the account grants : ."
Then we need to keep the example with code formatting.
The example text:
"RootFullAWSAccess-> allows * OU WorkloadsAllowOnlyApprovedSvc -> allows ec2:, s3:, ... (no dynamodb:) Prod acct FullAWSAccess-> allows * Result in Prod: dynamodb: is denied. The OU level never allowed it. This is why the default FullAWSAccess SCP exists at every level. If you replace it with an allow-list at the OU, that allow-list becomes the ceiling for every account underneath. Most common SCP incident: Someone detaches FullAWSAccess from an OU while testing an allow-list SCP, and every account under that OU loses access to services that weren't on the list. Prefer deny-list SCPs (explicit Deny statements on top of FullAWSAccess ) unless you have a specific reason to allow-list."
We need to wrap identifiers in backticks: FullAWSAccess, AllowOnlyApprovedSvc, etc. Also the arrow "->" maybe keep as is.
We'll convert: FullAWSAccess, AllowOnlyApprovedSvc.
Also the wildcard "*": we can keep as is or wrap? It's not an identifier; we can leave.
We need to preserve the exact text but we can add backticks around identifiers. That is a light polish; allowed.
Now heading: ## How a Request Is Evaluated
Paragraph: "For a principal in a member account, access requires an Allow from every applicable layer:" then bullet list.
We need bullet list:
- Identity-based policy on the user or role must allow the action.
- SCPs in the account's path must allow it.
- RCPs in the path must allow it for the target resource (if the service supports RCPs).
- Permission boundaries, session policies, and resource-based policies still apply as usual.
- An explicit Deny at any layer wins.
- SCPs and RCPs only ever reduce what is possible; they never grant access.
- A practical consequence: when you debug an AccessDenied in a member account and the IAM policy looks correct, check the SCPs on every node in the account's path, not just the ones attached directly to the account.
We need to keep the bullet list.
Now heading: ## Landing Zone Pattern
Paragraph: "The goal is to separate the governance plane from workloads:" then bullet list:
- Management account: org and billing only. Tightly restricted human access.
- Security OU: Log Archive receives org-wide CloudTrail and Config data. Security Tooling (Control Tower calls it Audit) is the delegated administrator for GuardDuty, Security Hub, and similar services.
- Infrastructure OU: networking and shared services.
- Workloads OU: prod and non-prod split so guardrails can differ.
- AWS Control Tower orchestrates this on top of Organizations: Account Factory for provisioning, managed controls (preventive via SCPs/RCPs, detective via AWS Config, proactive via CloudFormation hooks), and drift detection.
- If you manage infrastructure as code, Account Factory for Terraform (AFT) provisions accounts through a Terraform pipeline.
We need to keep that.
Then heading: ## Operating Lifecycle
Paragraph: "Identity: IAM Identity Center for human access, cross-account roles for automation, least privilege throughout." then bullet list? Actually the text continues: "Provisioning: Account Factory, AFT, or the Organizations API. Never click-ops accounts in production. Baseline: logging, Config, security services, and networking applied from day one, typically via StackSets or Control Tower. Guardrails: preventive, detective, and proactive controls, plus drift detection. Lifecycle: move accounts between OUs as their purpose changes. To retire one, move it to a Suspended OU with a deny-all SCP, then close it. A closed account can be reopened during the post-closure period (90 days)."
We can keep as paragraphs or bullet list. The original had line breaks but not bullet list. We'll keep as sentences separated by spaces? The original: "Identity: IAM Identity Center for human access, cross-account roles for automation, least privilege throughout. Provisioning: Account Factory, AFT, or the Organizations API. Never click-ops accounts in production. Baseline: logging, Config, security services, and networking applied from day one, typically via StackSets or Control Tower. Guardrails: preventive, detective, and proactive controls, plus drift detection. Lifecycle: move accounts between OUs as their purpose changes. To retire one, move it to a Suspended OU with a deny-all SCP, then close it. A closed account can be reopened during the post-closure period (90 days)."
We'll keep as separate sentences; we can break into paragraphs for readability.
Now heading: ## Production Gotchas
Paragraph: "SCPs and RCPs never restrict the management account. Anything you run there is outside your guardrails. That is the main reason to keep workloads out of it. SCPs don
Comments
No comments yet. Start the discussion.