Published on

Divide and Rule

Authors
  • avatar
    Name
    Benjamin Lee
    Twitter

A single cloud account is a single point of failure. Splitting it into many is less enterprise theatre than plain self-preservation.

Somewhere a developer is fat-fingering an S3 bucket into public view. Somewhere else a leaked access key is quietly mining cryptocurrency on someone else's dollar, and a runaway job is turning a modest monthly bill into a memorable one. If all of that is happening inside one AWS account, it is all happening to the same business at once. That is the trouble with putting everything in one place: everything can go wrong in one place too.

AWS itself is direct about this: using multiple accounts to isolate and manage business applications and data helps you optimise across most of the Well-Architected Framework pillars — operational excellence, security, reliability, and cost optimization. An AWS account is the fundamental isolation boundary for identity, access management, and billing. Resources in one account cannot access resources in another by default.

A single account means a single blast radius. A compromised credential, a runaway cost spike, or a misconfigured S3 bucket can affect everything. The fix isn't complicated. But it does require setting things up deliberately.

Not the org chart

AWS Organizations, Amazon's account-management service, lets you group accounts into organisational units, or OUs. The key design principle: OUs should be based on function and required controls, not your org chart. Model your boundaries on how marketing sits next to finance and you will regret it; model them on what each set of accounts is allowed to do, and the guardrails write themselves.

A practical starting structure:

Root
├── Security OU
│   ├── Log Archive Account     (centralized CloudTrail, S3 access logs)
│   └── Audit Account           (Security Hub, GuardDuty, Config aggregator)
├── Infrastructure OU
│   └── Shared Services Account (CI/CD, artifact registries, DNS)
├── Workloads OU
│   ├── Development Account
│   ├── Staging Account
│   └── Production Account
└── Sandbox OU
    └── Individual developer sandbox accounts

The Security OU is special — AWS Control Tower, Amazon's account-governance service, provisions a Log Archive and an Audit account automatically, and recommends keeping them clean, meaning no non-security workloads land there. Think of it as the room where the auditors sit. Nobody else gets a desk.

Let the tower do the heavy lifting

Control Tower automates the baseline: it provisions your management account, Log Archive, and Audit accounts, wires up CloudTrail and AWS Config across all accounts, and sets up IAM Identity Center for federated access. The official guidance is to use Control Tower as your starting point and extend from there with custom SCPs and Account Factory for Terraform (AFT).

Account Factory for Terraform lets you define new accounts as code:

module "staging_account" {
  source  = "aws-ia/control_tower_account_factory/aws"
  version = "~> 1.0"

  account_name              = "workload-staging"
  account_email             = "aws+staging@yourcompany.com"
  organizational_unit_name  = "Workloads"
  sso_user_email            = "admin@yourcompany.com"
}

This makes account provisioning repeatable, auditable, and fast — a new isolated account in minutes instead of days.

The rules that earn their keep

An SCP, or service control policy, defines the ceiling of what any IAM principal in an account can do, regardless of their own permissions. It grants nothing. It only restricts. AWS recommends applying less granular policies higher in the OU hierarchy and tightening them as you move to leaf accounts.

Three SCPs are worth applying from day one.

Require MFA for sensitive actions:

{
  "Effect": "Deny",
  "Action": ["iam:DeletePolicy", "iam:DeleteRole", "iam:AttachRolePolicy"],
  "Resource": "*",
  "Condition": {
    "BoolIfExists": {"aws:MultiFactorAuthPresent": "false"}
  }
}

Block root account usage:

{
  "Effect": "Deny",
  "Action": "*",
  "Resource": "*",
  "Condition": {
    "StringLike": {"aws:PrincipalArn": "arn:aws:iam::*:root"}
  }
}

Restrict to approved regions:

{
  "Effect": "Deny",
  "NotAction": ["iam:*", "organizations:*", "support:*"],
  "Resource": "*",
  "Condition": {
    "StringNotEquals": {
      "aws:RequestedRegion": ["us-east-1", "us-west-2"]
    }
  }
}

Apply these at the Root or at the Workloads OU level — not per account. That is the whole point: write the rule once, enforce it everywhere below.

Who gets in, and how

For human access, IAM Identity Center — the successor to AWS SSO — is the right answer. Define permission sets, assign them to accounts, and users get time-limited credentials scoped to exactly what they need. No long-lived IAM users. No shared credentials.

For CI/CD and service-to-service access, use IAM roles with OIDC trust. GitHub Actions, Microsoft's automation platform, is a typical case:

- uses: aws-actions/configure-aws-credentials@v4
  with:
    role-to-assume: arn:aws:iam::${{ secrets.PROD_ACCOUNT_ID }}:role/GitHubDeploy
    aws-region: us-east-1

The trust policy on that role scopes it to a specific GitHub repo and branch — no secret rotation required, no long-lived keys at risk.

The payoff

Here is the dividend. The blast radius of a credential compromise in a multi-account setup is one account — and SCPs further constrain what an attacker can do even within that account. Billing is isolated by account, so cost anomalies surface immediately. Developers can experiment freely in sandboxes without touching production infrastructure.

The overhead to set this up with Control Tower is measured in days. The overhead to migrate a tangled single-account setup later is measured in months. The arithmetic, for once, is not subtle. Set it up first.


Sources: