Cloud & identity

Streamlining Cloud Architecture: Automating Multi-Account AWS Environments

One AWS account per domain per environment, created programmatically with Terraform, governed by Service Control Policies, and registered in the CMDB — with three corrections to the standard multi-account pitch: no per-account free tier, SCPs as deny-only guardrails, and cleanup as a separate system.

In brief

The key idea

One AWS account per domain per environment, created programmatically with Terraform, governed by Service Control Policies, and registered in the CMDB — with three corrections to the standard multi-account pitch: no per-account free tier, SCPs as deny-only guardrails, and cleanup as a separate system.

Intro — summary

Status: DRAFT — design stage, October 28, 2026. Nothing in this post has been provisioned yet. The AWS Organization described below was verified live on October 28, 2026 (read-only). The workflow is designed and awaiting approval; the pilot has not run. This post will be revised with pilot evidence before any production claim is made about it. It follows the factory rule: test first, then production, with approval at each gate.

Managing a growing cloud footprint eventually forces a choice: one big shared AWS account where every workload, environment, and experiment shares the same bill and the same blast radius — or many small accounts, one per domain per environment, each isolated by the strongest boundary AWS offers. This post documents the design we chose for the domain factory: one AWS account per domain per environment level, created programmatically with Terraform, governed by Service Control Policies, and registered in our CMDB — and it corrects the three claims the usual multi-account pitch gets wrong: that every new account brings its own free tier, that SCPs alone control cost, and that cleanup takes care of itself. The short version: account creation is free, but there are no per-account credits; SCPs are deny-only guardrails, not spend caps; and closing accounts has 90-day, quota, and email consequences that must be designed for up front. The full design, with its guardrails and rollout plan, is below.

What this post covers: (1) the real starting point — an Organization that already exists, with three accounts and one proven SCP; (2) why separate accounts, and what separation does and does not buy; (3) the design — environment levels as a list, one OU per level, naming/email/tagging standards, SCPs, budgets, and CMDB registration; (4) the three corrections to the standard pitch; (5) the guardrails that make 140 accounts safe rather than 140 liabilities; (6) the rollout — one pilot domain first, then the rest in batches; (7) what is still unverified, and what would change the design.

The details

1. Where we start: an Organization that already exists

We did not start from zero. A read-only discovery pass on October 28, 2026 confirmed:

  • An AWS Organization exists: ID o-i953e90ayx, feature set ALL (all features enabled — the prerequisite for SCPs), one root, no organizational units yet.
  • Three member accounts, all directly under the root: the management account (169952954347), a prdc-vzxy account (2024), and a Sandbox account (279199950628, 2022). Sandbox is a member account, not the management account — a fact that shapes the whole design, because only the management account can create new member accounts.
  • Two SCPs already defined: AWS-managed FullAWSAccess (attached at the root) and a customer-managed SCP that denies one specific Bedrock model in Sandbox, written after a Cost Anomaly Detection alert in August 2026, with a runbook in our SOPs repo. SCPs are not a new idea here — they are a proven cost-governance tool in this exact Organization, and the design builds on that precedent.

That starting point is good news: no migration, no feature-set upgrade, no greenfield org. The work is structure (OUs), automation (Terraform + Actions), and guardrails (SCPs, budgets, lifecycle) on top of a working foundation.

2. Why separate accounts

An AWS account is the strongest isolation boundary the platform offers: separate IAM, separate service quotas, separate bill split, separate blast radius. For a factory managing 35 domains, that buys three things:

  • Blast-radius isolation. A bad deploy, a compromised key, or a runaway workload in one domain's dev account cannot touch another domain's production account. No shared credentials, no shared quotas.
  • Cost visibility. Consolidated billing puts every account on one bill paid by the management account — separation alone does not attribute cost. What makes cost visible is per-account Cost Explorer views plus cost allocation tags (Domain, Environment) that are applied and activated. Separation makes attribution possible; tags make it real.
  • Governance that scales. Policies attached at the OU level cover every current and future account in that OU. One policy set per environment level governs 35 accounts each, with no per-account attachment work.

The price of those benefits is surface area: 35 domains × 4 environment levels = up to 140 accounts, each needing a unique root email, a budget, and a lifecycle. The rest of this post is about paying that price deliberately, once, in automation — instead of paying it forever in manual overhead.

3. The design

Environment levels are a list, not a shape. The automation takes levels as a variable — default ["dev", "test", "stage", "prod"]. Adding or removing a level is a one-line change, not a re-architecture. We default to four levels because stage buys a prod-parity rehearsal environment (release rehearsals, database migrations, DNS cutovers, client acceptance) that test — where developers break things — cannot honestly provide. The honest case for three levels is real: for a purely static site with atomic deploys and no acceptance step, stage adds surface without value, and dropping it is the same one-line change. The pilot decides the default per domain type; the list stays parameterized either way.

One OU per environment level. Four OUs under the root (dev, test, stage, prod). SCPs and budget scoping attach at the OU, so 140 accounts need no per-account policy attachments. The three pre-existing accounts stay directly under the root, untouched — the factory does not reorganize what already works.

Naming, email, and tags are deterministic. Account names follow <domain-slug>-<env> (for example, stoneward-systems-prod). Root emails follow a single scheme validated against the longest domain in the portfolio (glaciermicroelectronics.com, 27 characters): the proposed pattern fits comfortably under AWS's 64-character email cap, with deliverability and root-recovery checks run in the pilot before any rollout. Every account carries the same tags: Domain, Environment, ManagedBy=domain-factory, Owner. Tags are what make the consolidated bill attributable — and what make cleanup possible later.

Admin access is designed, not improvised. Account creation runs from the management account (only it can call CreateAccount). Terraform uses a dedicated, least-privilege admin identity in the management account whose credentials live in AWS Secrets Manager and are referenced as sensitive Terraform Cloud variables — never in chat, never in the repo. New accounts arrive with the standard OrganizationAccountAccessRole for bootstrap, then least-privilege roles. Root MFA enablement and root-mailbox recoverability are pilot checklist items, not assumptions.

Every account is registered in the CMDB. Creation writes a record to NetBox (our working CMDB): account ID, domain, environment, OU, email, SCP attachments. An account that exists in AWS but not in NetBox is, by definition, an unauthorized asset under CIS Control 1 — and the reconciliation job that finds such accounts is part of the design, not an afterthought.

4. Three corrections to the standard pitch

Correction 1: there is no per-account free tier anymore. The classic "12 months free per new account" ended for accounts created on or after July 15, 2025. The current model offers promotional credits (up to $200) to a single eligible account, Free Plan accounts expire after six months or when credits run out, and — decisively — enrolling in an Organization upgrades a Free Plan account to a Paid Plan and aggregates usage org-wide, with one org-wide benefit, not one per account. Our planning assumption is therefore ~$0 incremental free-tier value from new member accounts. Account creation itself is free; everything inside an account bills pay-as-you-go to the management account from the first resource. Anyone budgeting "$200 × 140 accounts" of credits is budgeting fiction. (The pilot's first bill is the final verification of this reading.)

Correction 2: SCPs are guardrails, not controls. SCPs are deny-only: they set the maximum permissions available, they grant nothing, they do not apply to the management account, and they cannot cap spend in dollars — there is no cost-based SCP condition. What they do well is deny: expensive instance families, unapproved regions, root-user use, leaving the Organization, disabling CloudTrail/Config/GuardDuty, and creating untagged resources. "Strict governance" means an allow-list SCP design tested per OU — not merely attaching FullAWSAccess and calling it governance. Cost enforcement is Budgets (alerts) plus SCPs (prevention) plus cleanup (removal) — three layers, and SCPs are only one of them.

Correction 3: cleanup is a separate system, and closing is not free. SCPs prevent provisioning; they delete nothing. Forgotten member accounts keep billing — they are Paid Plan accounts with no auto-close. Closing an account starts a 90-day post-closure window, closed accounts still count against quota until permanently closed, bulk closure is capped (20% of member accounts or 250 per 30 days), and a closed account's root email cannot simply be reused for its replacement. Terraform's default destroy of an Organizations account only removes it from the org (it becomes a standalone billable account) — closing requires close_on_deletion = true, set deliberately. The design therefore includes a lifecycle routine — quarterly review, idle detection, a defined decommission procedure — because without one, 140 accounts will quietly include accounts nobody remembers.

5. Guardrails that make 140 accounts safe

  • Preventive (SCPs, OU level): deny costly instance families; deny non-approved regions (with exemptions for global services); deny root-user use; deny leaving the Organization; deny disabling CloudTrail, Config, and GuardDuty; require tags on resource creation.
  • Detective (budgets + anomaly detection, alert-only and free): one budget per linked account (140 is far under the 20,000 limit), Cost Anomaly Detection for the Organization. These alert; they do not stop spend — that limit is stated, not hidden.
  • Lifecycle (the gap SCPs cannot fill): scheduled idle-resource sweeps, quarterly account review, a written decommission procedure honoring the close quota and email consequences, and close_on_deletion = true on every factory-created account.
  • Reconciliation (CIS Control 1): periodic comparison of Organization member accounts against NetBox records; any account without a CMDB record is flagged as unauthorized.
  • Prerequisites before rollout: the default account quota is 10 — a quota increase (requested from the management account only, to ≥150 with headroom) is a gating prerequisite, and account creation is capped at 5 concurrent, so rollout is a batched job, not an instant apply.

6. Rollout: one pilot domain, then the rest

The pilot runs on one domain end to end — stonewardsystems.com, the factory's reference domain: quota increase granted, four accounts created via Terraform, env-OU SCPs and per-account budgets attached, a trivial workload deployed in each, Cost Explorer verified per account, and one throwaway account taken through the full close cycle to prove the 90-day, quota, and email consequences before they matter. Only then do we decide the final level default and detection-service scope, and only then does the rollout proceed in batches across the remaining 34 domains, with a merge-gated workflow and a CMDB write per account. The pilot is what de-risks the two unknowns that actually matter: whether the email/root-access scheme survives a full create-use-close cycle, and what the true per-account baseline bill is with the chosen detection services on.

7. What is still unverified

  • The exact free-tier treatment of Organization-created accounts (pilot bill + Billing console check).
  • Terraform registry argument lists at the pinned provider version (to be fetched before the module is written).
  • Whether a payment-method gate applies at account creation for this management account (pilot attempt).
  • Whether a closed account's root email is burned permanently or only during the 90-day window (treated as permanent until AWS Support confirms otherwise).
  • The cost of a Control Tower baseline at 140 accounts — deferred deliberately: Control Tower and its Account Factory add a management account, pipelines, and per-account Config recording costs that this scale does not yet need. Raw Organizations + Terraform is the starting point; Control Tower is revisited if compliance needs grow.

References

Publication context

Originally published October 28, 2026. Product capabilities, platform names, and technical steps may have changed. Confirm current details before making an implementation decision.

Put the idea to work

Turn insight into a practical next step.

Explore how SJULTRA can help with cloud platform security assessment, or talk through your specific environment with the team.

Let’s see a clearer tomorrow.

Talk with a team that turns complex security challenges into a practical next step.

Talk to SJULTRA