Concept 10. By Nicolas Dao · Last reviewed

One AWS account, or many?

Should everything run in one AWS account with careful policies, or behind walls of accounts, one per environment and workload, with the backups in an account of their own?

What you'll be able to decide

Whether a system runs in one AWS account or in several; where each wall goes (per environment, per workload that can hurt another, per client, and a backup account production can't write over); what each wall stops; and how to cross one on purpose.

An account is the hardest wall AWS has: two accounts can't touch each other unless someone allows it. Inside one account, only IAM policies stand between a leaked key or a wrong command and production, and some limits are shared by everything in it: Lambda's concurrency pool, SES's sending reputation. So put a wall wherever one thing must not be able to hurt another: production apart from dev and staging, a workload that can damage a shared limit in its own account, a client's systems in the client's accounts, and the backups in an account production can't write over. Organizations costs nothing extra and Savings Plans still apply across the accounts, so the cost of the walls is setup, not the bill.

In short

The options

  1. A. One account, careful policies

    Every environment and workload side by side, told apart by names and tags. Everything rests on every IAM policy being exactly right.

  2. B. An account per environment

    An empty management account, then dev, staging and prod in accounts of their own, with one sign-in for people. A mistake or a leaked key in one can't touch the others.

  3. C. An account per workload, and per client

    A workload that can damage a limit it shares with the others, such as email reputation, in its own account; and each client's systems in the client's own accounts.

  4. D. A backup account

    A copy of every recovery point in a locked vault, in an account the production credentials can't reach.

The rule of thumb

An account is the hardest wall AWS has. Inside one account, everything rests on every IAM policy being exactly right.

From AWS Organizations + IAM Identity Center

How the answer changes with scale

  1. One app and a small team

    A management account, an account each for dev, staging and prod, and a backup account. Organizations costs nothing extra, so being small is no reason to share one.

  2. A workload that can damage a limit it shares with another

    An account of its own: marketing mail apart from sign-in mail.

  3. Teams that pay their own bills

    Restrict each Savings Plan to the account that bought it, or group accounts with Cost Categories.

  4. Dozens of accounts, vended by a central security team

    Control Tower earns its place, with AWS's prebuilt controls. Below that, Organizations, a handful of SCPs and Identity Center in code.

The problem, in plain words

Inside one AWS account, everything can reach everything else unless a policy says no. The production database, the staging API, a developer's experiment and last night's backups sit side by side, and the only thing between them is IAM: every policy, on every role and every user, exactly right. That's the hardest part of AWS to get right. Getting policies least-privilege by hand was a nightmare for years, and today they're largely the agent's problem, with a human reviewing the result. One wrong policy still reaches everything in the account.

An account also shares its limits. Lambda's concurrency is one pool for every function in the account and Region. SES's sending reputation is per account and region. So a job in staging can throttle production, and a newsletter can stop the password resets.

An account is the one wall that doesn't depend on a policy being right: two accounts can't touch each other unless someone allows it. So the question isn't "how careful are our policies?" It's where the walls go.

The picture

The same two accidents, three ways: a developer's key that leaks, and production credentials that get stolen.

  1. One account

    1. Leaked developer key
    2. One account: dev, staging and prod
      • Production data
      • Production's secrets
      • The backups

    Everything rests on every IAM policy being exactly right.

  2. An account per environment

    1. Leaked developer key
    2. The damage stays in the dev account
    3. Reach into production

    Two accounts can't touch each other unless someone allows it.

  3. A backup account

    1. Stolen production credentials
    2. Production data deleted
    3. Locked vault in the backup account
    4. Restored

    A copy production can't write over survives ransomware and a fat-fingered admin.

Two accidents, three ways: a leaked developer key in one account, where only IAM policies stand between it and production's data, secrets and backups; the same key with an account per environment, where it stops in the development account; and stolen production credentials with a backup account, where a locked copy survives the loss of production.

The options

One account, with careful policies. Everything in one place, told apart by names and tags. I don't build anything a client depends on this way. Everything rests on every IAM policy being exactly right, the limits are shared, and the backups sit within reach of the credentials they're meant to survive. The alternative costs nothing extra: AWS Organizations and IAM Identity Center have no extra charge.

An account per environment. My default, and non-negotiable on every AWS engagement. This is how I wire it:

  • A management account that holds nothing but Organizations, Identity Center and the bill.
  • An account each for dev, staging and prod, so a mistake or a leaked key in one can't touch the others.
  • A few service control policies (SCPs): nobody leaves the organization, nobody turns off CloudTrail, nothing runs outside the Regions the client uses.
  • One sign-in for every person through Identity Center, with permissions per account and short-lived credentials. No access keys anywhere.

The code follows the walls. Pulumi has one project per app and one stack per environment, and the pipeline deploys each environment with its own OIDC role in its own AWS account (GitHub Actions). When I moved an agriculture-technology platform off a single server (the project), it landed on accounts separated by environment, with single sign-on instead of shared passwords.

An account per workload, and per client. Some limits belong to the whole account, and some of them can't be fixed inside it. SES's reputation is one of those: at 5% bounces or 0.1% complaints the account goes under review, and at 10% or 0.5% AWS can pause sending, for every app in it. So marketing goes in a separate account from transactional mail, and a campaign can't take down sign-in. Where a shared limit does have a fix inside the account, use it: Lambda's pool has reserved concurrency, capacity no other function can take.

Per client is the same wall, one level up. A client's accounts sit under the client's own organization, with one bill and one set of guardrails. Everything I build is in the client's AWS accounts and repositories, documented, so their team can take it over. Splitting doesn't cost the discounts either: Savings Plans and Reserved Instances apply across every account in the organization, and Cost Explorer splits the bill by account.

A backup account. The account that survives the others. AWS Backup copies every recovery point into a locked vault in a separate account. The separate account matters more than the second Region: ransomware and a fat-fingered admin both start with credentials in the production account, and a copy in another account, in a vault even the root user can't empty, survives both. Cross-account copies only work inside one organization, one more reason the organization comes first. RDS point-in-time restore doesn't copy to another account, so plan two recovery points: point-in-time restore in production for "undo the last hour", and daily snapshot copies in the backup account for "the account is gone".

The one account with no wall above it. SCPs don't apply to anyone in the management account. Whatever runs there escapes every guardrail you write. Don't put a workload there, however small: keep it to Organizations, Identity Center and billing.

A concrete scenario

A Sydney company runs a booking platform on AWS. Its contracts keep customer data in Australia, so its email goes through SES in Sydney. Everything is in one account: dev, staging and prod told apart by name prefixes, an IAM user key on each developer's laptop, password resets and the monthly newsletter sent from the same account, and the database's snapshots next to the database.

What goes wrong In one account Behind walls
A developer's laptop key leaks It's as far from the production database as one IAM policy It reaches the dev account at most. With Identity Center, there's no long-lived key to leak in the first place
The newsletter goes to a badly imported list Bounces pass 5% and the account goes under review; at 10%, AWS can pause sending, password resets included The marketing account is paused. Sign-in mail keeps going from production
A reprocessing job in staging floods Lambda It drains the pool production's API draws on: 1,000 concurrent executions by default, shared by the whole account and Region Staging has a pool of its own. In production, reserved concurrency guards the functions that matter most
An admin's production credentials are stolen The snapshots are in the same account, within reach The copies sit in a locked vault in the backup account
A Savings Plan is bought for production One account, one bill: nothing to split Still one bill. The discount floats to any account by default; restrict it if teams pay their own

None of these needed a new service. They needed walls: an empty management account, three environment accounts, one for marketing mail and a backup account, under one organization, on one bill.

The trade-offs

One account An account per environment Per workload and client A backup account
What a leaked key reaches Whatever the policies didn't forbid Its own environment Its own workload Not the copies: production's credentials can't write over them
Shared limits (Lambda's pool, SES's reputation) Shared by everything One set per environment One set per workload Not applicable
What it takes Nothing to set up; every policy right, forever An organization (no extra charge), each account in code More accounts to switch between A customer-managed KMS key shared with it, and a dedicated vault
Crossing a wall There's no wall A grant you write: a role, a key policy The same Copies only inside one organization
The bill One line Split by account; Savings Plans apply across every account The same, with plans restricted where teams pay their own One more account on the same bill

The rule of thumb

Put a wall wherever one thing must not be able to hurt another. An account is the hardest wall AWS has; inside one, everything rests on every IAM policy being exactly right. So production lives alone. A workload that can break a limit it shares gets its own account. A client's systems live in the client's accounts. The backups live where production's credentials can't reach them. And the management account stays empty, because it's the one account no guardrail applies to.

When the answer changes with scale

  • One app, a small team: a management account, dev, staging and prod, and a backup account. Organizations and Identity Center cost nothing extra, so being small is no reason to share one account.
  • A second workload that can hurt the first: its own account. Marketing mail is the classic case, because SES's reputation can't be split inside an account.
  • Teams that pay their own bills: by default the discount floats to any account in the organization, so the plan bought for production can end up discounting someone's test cluster. Restrict the plan to the buying account, or group accounts with Cost Categories. Turn Cost Explorer on in the management account, with the accounts and environment tags activated from day one.
  • Past ten accounts: a new organization can hold 10 accounts by default, and a closed account keeps counting until it's permanently closed, 90 days later. Request the increase from the management account before the plan needs it.
  • Dozens of accounts, vended by a central security team: that's when Control Tower earns its place, with AWS's prebuilt controls. I haven't needed it. For my clients' size, Organizations, a handful of SCPs and Identity Center in code do the same job with less machinery.

Crossing a wall on purpose

A wall is only worth it if every crossing is deliberate. These are the ones that catch people:

  • Your own keys, from day one. A secret encrypted with the default aws/secretsmanager key can't be shared with another account, and cross-account backup copies of RDS, Aurora and EBS fail with AWS-managed KMS keys, because those keys can't be shared. Create every secret, and encrypt every source, on a customer-managed KMS key shared with the account that needs it.
  • People through Identity Center, pipelines through roles. People get permission sets per account. CI gets OIDC federation to an IAM role in each account, never an Identity Center user and never a stored key.
  • Know which account you're in. A consultant lives in many AWS accounts at once: each client, each environment, each role, and it's far too easy to run a command against production when you meant staging. switch-profile, my own tool, checks who you really are on every switch, and production accounts get a red PROD badge. Scripts and agents prefix every command with the profile and never rely on [default].
  • No root user in member accounts. Turn on centralised root access in IAM and remove root credentials from member accounts. New accounts are then created without them.
  • Test a guardrail before it's everywhere. An SCP attached at the root that denies too much locks every member account out at once. Try it on a sandbox OU, move accounts in a few at a time, and keep SCPs in Pulumi, so a bad one is one revert away.
  • Set up each new account. New accounts start below 1,000 concurrent Lambda executions: raise the quota before launch. An account that sends email needs SES production access and its pricing plan set: two CLI calls per region, in the runbook.
  • Close accounts carefully. Closing an account suspends it for 90 days, then AWS deletes its vaults, even locked ones. Keep the backup account out of any clean-up, and deny it organizations:LeaveOrganization with an SCP.

Questions that change the answer

  • What can a leaked key or a wrong command reach today? If the answer includes production, production needs an account of its own.

  • Which workloads share a limit that one of them can break, such as SES's reputation or Lambda's concurrency pool? A limit with no fix inside the account calls for a wall.

  • Where do the backups live, and could the production account's credentials delete them? If they could, they're not a backup yet.

  • Who pays for which part of the bill? Teams that pay their own need their Savings Plans restricted, or Cost Categories.

  • Does a central security team vend dozens of accounts? That's when Control Tower earns its place.

Doesn't cover

  • Least-privilege policies inside an account. The walls limit how far a wrong policy reaches; they don't write the policies. The AWS entry has the method: the agent writes the policy from the code, and IAM Access Analyzer checks it.

  • Where application secrets live and how the code reads them: that's the Secrets Manager entry.

  • One AWS account per customer of a SaaS product: that's a tenancy question, not covered here.

  • Networks shared between accounts: not covered here.

Check your call

Decide before you open each answer.

  1. A small startup runs its API, staging and a monthly newsletter on SES, all in one AWS account, with an IAM user key on each developer's laptop.

    A management account, then dev, staging and prod accounts, with the newsletter in an account of its own, and Identity Center for every person instead of keys. Organizations costs nothing extra; what the walls buy is that a leaked key or a bad list import stops at one account.

  2. A company keeps its Aurora snapshots and S3 versions in the production account. Its auditor asks what happens if an admin's credentials are stolen.

    Whatever those credentials reach, they can delete, the snapshots included. Copy every recovery point to a locked vault in a separate backup account, on a customer-managed KMS key shared with it, keep point-in-time restore in production for 'undo the last hour', and deny organizations:LeaveOrganization on the backup account.

  3. A platform team buys a Compute Savings Plan for production, then sees the discount land on a developer's test cluster in another account.

    That's the default: the discount floats to any account in the organization. If teams pay their own bills, restrict the plan to the buying account, or group accounts with Cost Categories.

  4. A central security team must vend dozens of accounts, with AWS's prebuilt controls.

    Control Tower earns its place there. For a smaller company, Organizations, a handful of SCPs and Identity Center in code do the same job with less machinery.

The technology on this site

Read next

  1. 08 Where may the data go? Must the data, the prompts and the logs stay in Australia, and what does each service do with them by default? Residency is a property of every hop a request takes, not of the app: the database, the files, the email, the analytics, the logs and the model. Most global platforms send data wherever they like by default; some keep it in Australia if you choose a region or a profile, sometimes for a premium; some have only a best-effort hint, or nothing. List every hop, check each one, and the answer is the weakest hop. Where it must stay, keep it in the client's own AWS account in Sydney; where it needn't, the global tools are simpler.
  2. 07 Pay for the work, or pay to be ready? Should this part of the system cost nothing when it's idle, or run all the time so it's always ready? Every service has a cost shape: a floor (what it costs doing nothing) and a slope (what each unit of work adds). Scale-to-zero has no floor and the steepest slope, plus a wait after a quiet spell. Always-on has a floor and a flatter slope. A commitment discounts the floor for years. Read two numbers, the load at 3 am and the average against the peak: near zero at night means pay per use; high all day means pay to be ready, then commit to the floor once it's proven.
  3. 09 Where should code nobody reviewed run? Where does code written by a model or a customer run, so it can't reach your credentials, your network or the next user's files? A sandbox is for code nobody on your team reviewed: code a model writes at runtime, or code a customer uploads. Reviewed code, even an agent's, runs in a normal Lambda, Worker or Fargate task. Unreviewed code must never run where your credentials live: a Lambda carries its role and environment variables, and its /tmp survives into the next invocation. Give each session its own walls (a container on Cloudflare Sandbox by default; a microVM on Lambda MicroVMs when the code must stay in Australia, reach a private VPC, use a bigger machine or keep its processes for hours) and put nothing secret inside: its traffic leaves through a proxy that adds the credentials, to the destinations it needs and no others.
  4. 11 Who holds your keys, and for how long? Should people, pipelines and apps carry long-lived keys or borrow short-lived roles, where does each secret live, and which code you didn't write runs next to them? Every credential answers three questions, usually by accident: who holds it, how long it works, and what else runs next to it. A long-lived key works from anywhere until someone revokes it, and outlives the person who made it. A borrowed role works for one session: a person through SSO, a pipeline through OIDC, a function through its own role, so there's nothing stored to leak. The secrets that can't be a role (a database password, a Stripe key, a model provider's key) go in the secret store, one job per key, read at runtime by reference. And the code you pull runs with all of them: a tag can be moved, so pin it to a SHA, a digest or a hash.

Want this stack in your AWS account?

Cloudless Labs is a Sydney consultancy. We migrate on-premises systems to AWS, build serverless apps on this stack, and coach teams to ship with Claude Code and Codex.

Talk to us

Contact Us

For general inquiries, please contact us at:

For e-learning capabilities, please contact: