Organizations, SCPs and the Account Boundary

Why the AWS account is the real isolation boundary, what a service control policy can and cannot do, and how to lay out accounts so a mistake stays inside one of them.

beginner 20 min lesson hands-on task included

There is one hard isolation boundary in AWS, and it is not the VPC, the tag, or the IAM policy. It is the account. Service quotas are per account. Billing rolls up per account. A resource name collision, a runaway script, a compromised credential — all of them stop at the account edge unless something explicitly bridges it.

Which makes “how many accounts, and which workload goes where” one of the highest-leverage decisions in AWS, and one that is painful to change later.


Topic 1: The Organization

ORGANIZATION TREE — POLICY ATTACHES AT ANY LEVEL AND INHERITS DOWNWARD Org Root OU: Workloads deny region ≠ eu-west-1 acct: prod deny leaving the OU, deny CloudTrail off acct: staging same guardrails, smaller quotas OU: Sandbox deny expensive families, budget alarm SCP what the account MAY do IAM what the role IS granted EFFECTIVE the overlap only An SCP that "allows" S3 grants nobody anything. The management account ignores SCPs. Do not run workloads in it.
Two separate mechanisms that look similar and do opposite jobs. The SCP sets the ceiling for a whole account; IAM grants inside it. Effective permission is only the overlap, which is why an SCP can never fix a missing IAM grant.

An organization has one management account (formerly “payer”) and any number of member accounts, arranged in organizational units. Three capabilities matter operationally:

  • Consolidated billing. One bill, and — importantly — usage aggregates for volume discounts and for Reserved Instance / Savings Plan sharing across the whole organization. Two accounts each running half a workload get the same tiering as one account running all of it.
  • Service control policies. Guardrails that cap what any principal in an account may do.
  • Trusted access and delegated administration. CloudTrail organization trails, Config aggregators, Security Hub, GuardDuty and IAM Access Analyzer can each be run from one delegated account across every member.

Two rules about the management account that people learn the hard way:

  1. Do not run workloads in it. SCPs do not apply to the management account. Anything you build there sits outside every guardrail you wrote.
  2. Lock it down hard — MFA on root, no access keys on root, alarm on any console login. It can create accounts, remove SCPs and read every bill; there is no more privileged position in your estate.

Topic 2: What an SCP Actually Does

An SCP grants nothing. It is a filter on what IAM in that account is permitted to grant. Effective permission = SCP ∩ identity policy (∩ boundary ∩ resource policy, as covered in the evaluation order).

That single sentence resolves nearly every SCP misunderstanding:

  • An SCP that “allows S3” does not give anyone S3 access. It merely does not block it.
  • Removing an action from an SCP Allow list denies it, even to an account administrator.
  • An SCP cannot grant a permission the account’s IAM does not grant, and cannot make root do something.

FullAWSAccess is attached by default at every level and allows everything, which is what makes the deny-list style work. Two styles:

// DENY LIST (recommended): keep FullAWSAccess, add targeted Denies
{
  "Version": "2012-10-17",
  "Statement": [{
    "Sid": "DenyRegionsOutsideEurope",
    "Effect": "Deny",
    "NotAction": [
      "iam:*", "sts:*", "organizations:*", "route53:*",
      "cloudfront:*", "waf:*", "support:*", "budgets:*", "ce:*"
    ],
    "Resource": "*",
    "Condition": {
      "StringNotEquals": { "aws:RequestedRegion": ["eu-west-1", "eu-central-1"] }
    }
  }]
}

The NotAction list is not optional decoration: global services are reached through a regional endpoint (usually us-east-1), so a naive region deny breaks IAM, Route 53 and CloudFront across the whole account. Missing that exemption is the single most common way a region-restriction SCP causes an outage on the day it is attached.

An allow-list style SCP (replace FullAWSAccess with an explicit Allow of only permitted services) is stricter and much higher maintenance — every new service adoption becomes an SCP change. Use it for sandbox and dead-end accounts, not for the accounts where your engineers work.

The five guardrails worth having on day one:

1. Deny leaving the organization        organizations:LeaveOrganization
2. Deny disabling security telemetry    cloudtrail:StopLogging, guardduty:Delete*,
                                        config:DeleteConfigurationRecorder
3. Deny deleting or altering the audit  s3:DeleteBucket / PutBucketPolicy on the
   log bucket                           log archive bucket
4. Deny regions you do not use          aws:RequestedRegion (with the exemptions above)
5. Deny root user actions in member      aws:PrincipalArn StringLike ":root"
   accounts

Each is a Deny, so none of them can be argued around by a local administrator — which is the entire point.

Two limits that shape design: 5 SCPs per OU or account, and 5,120 characters per policy. Deep OU trees with a policy at every level run out fast. Plan for a shallow tree and consolidated policies.


Topic 3: Account Layout

The layout that has become standard, and the reasoning:

Root
├── Security OU
│   ├── log-archive        ← CloudTrail + Config, write-only from others
│   └── security-tooling   ← GuardDuty, Security Hub, delegated admin
├── Infrastructure OU
│   ├── network           ← Transit Gateway, DX, shared VPCs (RAM)
│   └── shared-services   ← CI runners, container registries, AMI pipeline
├── Workloads OU
│   ├── Prod OU           ← one account per workload, or per team
│   └── NonProd OU        ← same shape, weaker quotas, cheaper guardrails
├── Sandbox OU            ← time-boxed, budget-capped, aggressive SCPs
└── Suspended OU          ← SCP denying everything; where accounts go to be closed

The rule for splitting accounts: split on blast radius and ownership, not on org chart aesthetics. Production and non-production always split — sharing an account means a load test can exhaust a quota that production needs. Anything with distinct compliance scope splits, because auditing a subset of an account is far more expensive than auditing a whole one.

What you pay for more accounts: more places to configure, more roles to wire, more base infrastructure. Which is precisely why the account baseline should be code from the start — Control Tower, Account Factory for Terraform, or your own module. The first three accounts are easy by hand and the tenth is not.


Topic 4: Sharing Across Accounts Without Undoing the Boundary

You will need to share things. Do it with the mechanisms designed for it:

NeedMechanismNote
One VPC used by several accountsResource Access Manager shares subnetsThe owner keeps the VPC; participants launch into it
Central network hubTransit Gateway shared via RAMRoute tables per environment keep dev out of prod
A private service consumed by othersPrivateLink / VPC endpoint serviceNo route table changes, no CIDR coordination
Read a bucket from another accountBucket policy + identity policyBoth sides required; consider s3:ResourceAccount conditions
Assume a role from CICross-account role + sts:ExternalIdSession name in CloudTrail identifies the caller
Encrypt shared dataCustomer-managed KMS key with a key policyAWS-managed keys cannot be shared cross-account

The condition key that closes the widest hole: aws:PrincipalOrgID. It lets you write a resource policy that permits your entire organization without listing account IDs — and, more importantly, denies everyone outside it even if an ARN leaks.

{
  "Effect": "Deny",
  "Principal": "*",
  "Action": "s3:*",
  "Resource": ["arn:aws:s3:::internal-artifacts", "arn:aws:s3:::internal-artifacts/*"],
  "Condition": { "StringNotEquals": { "aws:PrincipalOrgID": "o-abc123example" } }
}

Topic 5: Billing, Budgets and the Blast Radius of Cost

Cost is an availability concern: a runaway spend gets an account frozen, and a frozen account is an outage.

  • Cost allocation tags must be activated in the management account before they appear in reports. Tags applied before activation are not retroactive, which is why tagging discipline pays interest.
  • AWS Budgets with actions can do more than email — apply a deny SCP or stop instances at a threshold. In a sandbox OU that is the difference between a lesson and an invoice.
  • Cost Anomaly Detection catches the shape of the problem you did not predict, which is most of them.
  • Set a budget on every account at creation time, even a generous one. An account with no budget has no smoke detector.

This module deliberately stops there: the full treatment — the layer model, tagging strategy, right-sizing, commitments and Karpenter economics — is the Cloud Cost Optimization path, and duplicating it here would let the two drift apart.


Topic 6: Verifying Your Guardrails

A guardrail nobody tested is a comment.

# What policies actually apply to this account, including inherited ones?
aws organizations list-policies-for-target \
  --target-id 111122223333 --filter SERVICE_CONTROL_POLICY

# Walk the tree upward — inheritance is where surprises live
aws organizations list-parents --child-id 111122223333

# Does this specific call survive the SCPs? Test from inside the account.
aws ec2 describe-instances --region ap-southeast-2
# expect: AccessDenied ... with an explicit deny in a service control policy

That last error string is the one you want to see. When an SCP denial happens, the message says service control policy explicitly — which is why the reason clause in an AccessDenied is worth reading rather than skipping.

Try it yourself: attach the deny-regions SCP to a sandbox account, then try to create a resource in a blocked region. Then check that IAM, Route 53 and Support still work. If they do not, your NotAction list is incomplete — and you have just found the bug in a controlled way instead of during a production change window.

Common mistake: attaching a region-deny SCP to an OU containing accounts that already have resources in those regions. The SCP does not delete anything; it makes those resources unmanageable — you cannot describe them, modify them, or delete them, and they keep billing. Inventory first, migrate or delete, then attach. In that order, always.