Worked Example · AWS

AWS Worked Example — Splitting One Account Into Seven

A worked example of separating a single AWS account into an organisation — what forced it, the order the migration had to happen in, and the guardrail that made the structure hold.

This is an illustrative example. The company, timeline and figures are invented. The starting position — everything in one account because that is the account that existed first — is close to universal.


The situation#

A healthtech company, 60 engineers, five years old. Everything ran in one AWS account: production, development, the data platform, internal tools and whatever anyone had built for an experiment.

It had never been a decision. The account was created in year one and everything since had been added to it.

Two events forced the change within a month of each other.

A development script deleted a production database snapshot. The engineer had the permissions to do it because everyone had broad permissions, since restricting them inside a single account had proved impossible to do without breaking something. The snapshot was recoverable from a backup taken four hours earlier.

A customer's security questionnaire asked whether production and non-production were separated. The honest answer was no, and it stalled the deal.

What made it hard#

The obvious plan — create accounts and move things — was not available, and understanding why shaped the entire migration.

Nothing was inventoried. No tags, no ownership records. The first task was working out what the account contained. It contained 340 resources, and roughly 60 of them could not be attributed to any team or purpose.

Everything shared one network. Every service reached every other service by private IP, on the assumption of a flat network. Nobody knew which of those paths were needed and which were accidental.

Twelve service accounts had long-lived keys with permissions that had accumulated over five years. What each actually used was unknown.

The order#

The sequence mattered more than any individual step.

Weeks 1–2: inventory and tag. Every resource given an owner, an environment and a purpose. The 60 unattributable resources were tagged unknown and monitored for 30 days; 41 had no traffic and were deleted, 19 turned out to matter, one of which was the certificate renewal job.

Weeks 3–4: the organisation and the guardrails, before moving anything. This was the decision that made the rest hold. Seven accounts were created — management, security, shared services, production, staging, development, sandbox — and four service control policies were applied at the organisation level before a single workload moved:

  • Deny leaving the organisation
  • Deny disabling or altering the audit trail
  • Deny regions outside the two in use
  • Deny deleting the log archive

The team's instinct was to do this last, as a tidying step. Doing it first meant that everything created during the migration was created under the rules, rather than needing a second pass to bring it into line.

Weeks 5–7: development first. Deliberately the least risky, and it surfaced every problem the production move would hit: hardcoded IPs, cross-service calls nobody had documented, and three services that turned out to depend on a shared cache nobody owned. All of it was found in an environment where breaking things cost nothing.

Weeks 8–11: production, service by service. One service per change window, with the previous environment kept running until the new one was verified. Two moves were reverted and retried the following week.

Week 12: the log archive to the security account, and the audit trail redirected there from every account.

Weeks 13–14: identity. Central identity with roles assumed per account replaced the twelve long-lived keys. Each role was scoped to what 90 days of access records showed it had actually used, which was consistently a small fraction of what it had been granted.

What went wrong#

The staging account was almost skipped. The argument was that development plus production was enough separation for the customer questionnaire, and staging could come later. It was kept, and three weeks later a data migration test in staging did something that would have been unrecoverable in production.

Cross-account access was underestimated. Services that had reached each other freely now needed explicit roles and network paths. This added about two weeks that were not in the plan. It was also the point of the exercise: every one of those paths is now a decision that someone made, rather than a consequence of everything sharing a network.

One team lost a week to the region restriction. They had been using a third region for a proof of concept nobody had mentioned. The policy blocked it, which is the policy working correctly, and the conversation that followed was the first time anyone had discussed which regions the business operates in.

The result#

BeforeAfter
Accounts17
Long-lived access keys120
Permissions granted vs usedWildcards everywhereScoped to measured usage
Audit trailIn the same account as the workloadsSeparate account, workloads cannot reach it
Development able to affect productionYesNo
Elapsed14 weeks, one engineer roughly half time

The customer questionnaire was answered in week 11. The deal closed.

What was learned#

Set the guardrails before moving anything. Four service control policies took an afternoon and meant every account created afterwards was compliant by default. Applied at the end, they would have required a second pass over everything.

Inventory first. Two weeks of tagging found 41 resources that were pure cost and one that was quietly critical, and it made every later step a known quantity.

Move development first. It found every category of problem in an environment where each one was free.

Do not skip an account because the current requirement does not need it. Staging was nearly dropped for good reasons and justified itself within three weeks.

Scope permissions to measured usage, not to intent. Comparing granted against used turned a principle into a short, specific list, and the list was much shorter than anyone expected.

Back to AWS