---
title: "The account is not the boundary"
canonical_url: https://ruslanakchurin.dev/blog/account-not-boundary
description: "Porting the IaC operating model from GCP to AWS, mechanism by mechanism: account vending, grant and limit planes, regional network admission, and the readiness evidence that replaces a project-shaped handoff."
datePublished: 2026-07-16
dateModified: 2026-07-16
tags: ["infrastructure as code","amazon web services","aws organizations","identity and access management","cloud networking"]
about: [{"name":"Infrastructure as code","@type":"Thing","sameAs":"https://www.wikidata.org/wiki/Q24964334"},{"name":"Amazon Web Services","@type":"Thing","sameAs":"https://www.wikidata.org/wiki/Q456157"}]
mentions: [{"name":"Google Cloud Platform","@type":"Thing","sameAs":"https://www.wikidata.org/wiki/Q17054505"},{"name":"Terraform","@type":"SoftwareApplication","sameAs":"https://www.wikidata.org/wiki/Q28957072"},{"name":"Domain Name System","@type":"Thing","sameAs":"https://www.wikidata.org/wiki/Q8767"},{"name":"Key management","@type":"Thing","sameAs":"https://www.wikidata.org/wiki/Q1320561"}]
---
# The account is not the boundary

_Porting an IaC operating model from GCP to AWS._

Nothing about a well-shaped infrastructure codebase depends on the cloud underneath it. Ownership decides the cut, lower tiers publish and higher tiers consume, environment issues the {{workload boundary | The isolation unit a workload deploys into, plus everything that must hold before the workload can use it: proved access, admitted networks, observed audit, effective quotas. A project on GCP; an account plus its admitted capabilities on AWS. | Workload boundary}}, shared mutations carry binding rules. Those rules name no provider; moving between GCP and AWS changes none of them.

What changes is how much of one handoff stays visible. GCP lets a workload boundary look like a single object: a project sits under a folder; one environment component creates it, enables APIs, attaches deploy federation, grants Shared VPC use, and publishes the assigned reference inside the same apply. AWS refuses to bundle that. The same boundary spreads across an asynchronous account lifecycle, several policy planes, regional networks, and audit systems that converge on their own schedules. The protocol was always there. GCP compresses it into one handoff; AWS runs each step as a separately owned system — the entire difference, and it is operational, not conceptual.

GCP earns the compression honestly. For a startup, an embedded identity provider, a shared network built into the product, and an organisation hierarchy that works on day one make GCP the better cloud — the bundle is too good to ignore, and I would pick it again for a small team that needs its first boundary this week. It is still packaging; the model is the part that ports.

> [!NOTE]
> [Making IaC boring](/blog/making-iac-boring) develops these rules at length, built out on GCP: the three-tier cut, the contract tuple, tier membership, and the resolver gate. This article stands on its own. It follows one workload boundary from request to usable on AWS — the full translation matrix, the vending protocol and its provisioning sequence, the identity and network proof paths, a reference layout, the failure modes, the readiness evidence that gates publication, and the defaults that were rejected.

## Properties translate; products don't

The usual first step is a vocabulary swap — project becomes account, folder becomes OU, Shared VPC becomes the networking account — as if the port were a rename. Each of those analogies imports a GCP operating property into an AWS mechanism that does not carry it. What translates reliably is the property underneath: an isolation unit and its vending call, an inheritance plane, a shared network surface. The matrix carries all of them; the rest of this article walks the three that take the most weight.

| GCP → AWS | Not equivalent because | Operational consequence |
| --- | --- | --- |
| Organisation hierarchy → Organizations root and OUs | Folder IAM bindings grant access down the tree; OU-attached policies restrict and never grant | OU placement is policy and lifecycle metadata; the boundary is the account plus its admitted capabilities |
| Project as isolation unit → AWS account | An account is a stronger security and failure boundary than a project-shaped namespace; quotas and API rates stay scoped to the unit on both clouds | Account lifecycle stays outside application IaC, behind a separately authorised executor |
| Project vending → [`CreateAccount`](https://docs.aws.amazon.com/organizations/latest/APIReference/API_CreateAccount.html) | Creation is asynchronous, starts under the root, needs a unique root email, and installs a broad management-access role | The call returns a request handle, not an account; the ID arrives when creation succeeds, the assigned boundary only after reconciliation completes |
| Managed landing zone → Control Tower Account Factory | Service Catalog-backed account vending plus an enrolment lifecycle with baselines, controls, and drift | Stays a private implementation of the vending protocol, never the public contract |
| Terraform vending workflow → Account Factory for Terraform | Builds on Control Tower and Service Catalog; not an independent peer | Justified by an existing Control Tower lifecycle and a real customisation fleet |
| Cloud Identity groups → external IdP, Active Directory, or IAM Identity Center directory | Groups reach accounts through permission sets that materialise IAM roles per account | Authentication, group source, assignment, provisioned role, and session are separate steps to prove |
| Hierarchical IAM inheritance → identity, resource, trust, and key policies, plus [SCPs](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_policies_scps.html)/[RCPs](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_policies_rcps.html) and boundaries | Authorisation is an intersection and union across planes; SCPs and RCPs limit and never grant | Grants and limits stay separate; load-bearing paths are tested, not inferred from policy files |
| Workload service account → IAM role with service trust or workload federation | The permission policy is one side; trust, resource, and key policies may all be required | Workloads run on temporary credentials; static keys and IAM users stay outside the default |
| CI federation → OIDC/SAML provider plus STS assumption | Trust is account-local and has to be bootstrapped before the tenant pipeline exists | Bootstrap creates the tenant path, proves it, then expires its own broader authority |
| Global VPC → regional VPCs | VPCs are regional and subnets are zonal | Region becomes an explicit contract context and failure domain |
| Shared VPC → RAM-shared subnets | The owner controls the VPC, routes, NAT, endpoints, and resolver surfaces; participants own ENIs and security groups on participant quotas | Fits only where participants accept common operational fate and a two-stage teardown |
| Shared network with stronger isolation → per-account VPCs plus optional Transit Gateway | Connectivity becomes explicit attachments, route tables, IP allocation, and DNS associations | Buys account-owned lifecycle and a regional blast radius; transit waits for cross-boundary reachability |
| Private DNS → Route 53 private hosted zones plus Resolver rules and endpoints | Zone ownership, VPC association, forwarding, and cross-account sharing are distinct operations | DNS admission and teardown belong in the network contract |
| Organisation audit roots → a CloudTrail organization trail, delegated Config/Security Hub/GuardDuty, a log-archive account | Organisation-wide collection is opt-in per service, each with its own delegated-admin path | Audit readiness is observed per service, not assumed from the org tree |
| Billing and quotas → consolidated billing plus account- and region-scoped quotas | The management account pays, but limits stay account- and region-scoped | Readiness distinguishes requested increases from effective limits |
| KMS ownership → regional keys, key policies, grants, cross-account caller policies | Cross-account use needs permission on both the key policy and the caller's IAM path | KMS stays a separately owned capability, proved on the real cross-account path |
| IaC bootstrap → Organizations creator plus account-local execution roles | Creator credentials and account-local execution are different authorities with different lifetimes | Bootstrap trust is installed, proved, then expired |

Every row has the same structure: the mechanism exists on both sides, and the operating property changed thickness. Nothing in the two right-hand columns asks for a new rule. The account is the model's assigned boundary; the policy planes are its binding rule, with holder, operations, and surface finally spelled out by the provider itself; regionality is contract context.

## The account exists before the boundary is ready

On GCP, the gap between "project created" and "workload can deploy" is short enough to hide inside one apply. On AWS, that gap is the design problem. `CreateAccount` is asynchronous: the call acknowledges a request, the account ID arrives only when creation succeeds, and the account then needs OU placement, contacts, a security baseline, audit enrolment, identity materialisation, network admission per region, and quota verification — each owned by a different system, several of them eventually consistent. An account ID in hand proves almost nothing about the boundary.

This is the {{unready interval | The span between the provider acknowledging the isolation unit and the boundary actually holding: access proved, networks admitted, audit observed, quotas effective. | Unready interval}}, and the environment tier still owns it. The membership rule survives untouched: the lower tier issues the boundary and publishes the reference upward. What changes is that issuing stops being a create call and becomes reconciliation. Capability owners — account lifecycle, identity, security and audit, network, quota, cost — publish {{receipts | A capability owner's recorded proof of a named path: actor, operations, surface, probes run, what stays unproved, and when the proof expires. | Receipt}} for what they have proved. A small coordinator validates those receipts and links them; it owns none of the underlying resources and cannot approve its own evidence. The workload-facing product is one versioned `AssignedAccountRef` that appears only after every required claim holds. READY names that publication; every earlier state is platform-internal. Before READY there is no reference to consume, which is exactly how [the resolver](/blog/making-iac-boring/04-fail-before-apply) wants composition to fail: before any provider call, with the missing claim named.

> [!NOTE]
> The account is the isolation unit AWS issues; the boundary is that unit plus every claim proved around it. Boundary product, assigned boundary, boundary coordinator — each later compound refers to that composite, never to the bare account.

### The request is a product

A product team requests a boundary product: owner, environment, cost identity, required regions, and {{reachability intent | The outcome the workload declares — which peers, shared services, or internet paths it needs to reach, and what may reach it — with no mechanism attached. The network owner turns it into routes, attachments, endpoints, and DNS. | Reachability intent}}. The platform derives the access and network profiles from that product. A team that needs something different gets an explicit exception with its own approver; the default never widens. The product carries a version, and the derived profiles advance by generation. Acceptance is synchronous: the platform validates the request, checks the caller against the recorded owner, and stores the request durably before any provider call. It is also the last synchronous answer the requester gets.

One `AssignedAccountRef` covers one workload environment account. A workload that needs several accounts receives several {{separately versioned | Each reference carries its own contract version, advanced when what it attests changes: a profile moves a generation, a region is admitted or withdrawn. Republishing one account's reference never moves its siblings. | Reference version}} references and composes them above the boundary; a shared account is a common-fate exception with its own tenancy and exit contract.

Acceptance sets the pattern, and every later lifecycle operation repeats it. A request to change profile, quarantine, retire, or close arrives from a caller the platform can authenticate — a person signing in through the workforce path, or a pipeline presenting its federated identity — and has to match the owner recorded at acceptance. The match alone is not enough: each operation carries a separate permission, so owning a boundary does not by itself authorise closing it. OU and policy exceptions, root-posture changes, and closure go further and require an authority independent of the coordinator. Behind the checks sit two records: the {{request catalogue | The platform's durable record of boundaries and lifecycle requests: the caller, the operation, the decision, and the state each account has reached. | Request catalogue}} and the {{evidence ledger | Every receipt the capability owners have published, kept as the history the coordinator validates before publishing or withdrawing a boundary. | Evidence ledger}}. Both are control-plane data in their own right: if that history can be rewritten, the receipts stop being evidence.

### Choose the account factory deliberately

Three implementations can stand behind the same protocol; choosing one is an ownership comparison.

**Direct Organizations plus a reconciler.** The default here. It is the smallest machinery that exposes the true lifecycle: `CreateAccount` requests issued, account states observed, delegated stacks establishing security, audit, identity, and network readiness. The cost is that the platform itself owns the durable request record, recovery, drift classification, and evidence probes — the default holds only while that custom surface stays materially smaller than the managed alternatives.

**Control Tower Account Factory.** A managed landing-zone lifecycle built on Service Catalog provisioning: baselines, controls, enrolment, drift detection. It earns its second lifecycle when governance evidence and managed OU baselines are requirements rather than conveniences. [Brownfield enrolment](https://docs.aws.amazon.com/controltower/latest/userguide/enroll-account.html) needs prerequisite cleanup, keeps existing VPCs, and fails outright on existing Config records unless AWS allow-lists the account; auto-enrolment is eventually consistent and can take hours. Its default VPC design must not decide the network boundary by accident.

**Account Factory for Terraform.** AFT adds a GitOps request-and-customisation workflow on top of Control Tower: repositories, pipelines, Step Functions, Terraform state, failure notifications. It pays for itself against a real fleet of repeatable account customisations, not as a reflexive Terraform preference.

Direct Organizations avoids the Control Tower lifecycle; it does not avoid lifecycle work. A like-for-like comparison counts workflow state, baseline installation, drift detection, brownfield handling, evidence, recovery tooling, and staffing on both sides. Whichever side wins, a common adapter wrapping every factory stays unbuilt. The vending protocol is already the abstraction: a request goes in, an `AssignedAccountRef` comes out, and the factory between them stays private. An adapter interface earns its place only when a second implementation actually runs.

### Who owns which side

The split is the one [the membership rule](/blog/making-iac-boring/03-membership) drew, with AWS nouns. The platform owns:

- the root-email namespace and contact automation;
- the boundary-product version, request record, account ID, OU placement, billing metadata, and observed lifecycle;
- bootstrap trust, delegated administrators, audit enrolment, the security baseline, network admission, and readiness evidence;
- suspension, quarantine, retirement, and closure.

The product team owns:

- the workload intent: owner, environment, required regions, reachability, cost identity, and the lifecycle decision;
- application resources, bounded runtime identities, workload-local policies, security groups, and records on delegated surfaces;
- detaching its consumers during retirement, before the contract is withdrawn;
- nothing in the Organizations management plane.

### The provisioning sequence, stage by stage

Thirteen stages turn a request into a published boundary, and every stage names an owner. Owner means a responsibility, not a squad, a division, or a department: in a startup one person usually holds all of them, and the collapse loses nothing — the responsibilities stay distinct however few heads carry them. All but one stage existed on GCP too, folded invisibly into a single apply. The exception is root posture: a GCP project has no root user to secure, and every AWS account ships with one.

```svg path="standalone/diagrams/01-provisioning-sequence.diagram.ts" width="600"
accept request → allocate identity → create account → place → contacts and root posture → baseline → workforce access → CI and workload trust → regional networks → audit and KMS → cost and quotas → retire bootstrap → publish AssignedAccountRef. Each stage carries its owner and timing. Any failed stage parks in quarantine with evidence kept and resumes; rejection happens only at acceptance, before any provider call.
```

Completion is observed, never inferred:

1. **Accept request.** Actor, immutable request ID, owner, and product version recorded.
2. **Allocate account identity.** Unique root email, account name, and profile generation reserved.
3. **Create account.** Creation status succeeds; the account reaches an active state.
4. **Place account.** The observed OU matches the desired one, with inherited controls enumerated.
5. **Set contacts and root posture.** Primary and alternate contacts observed; centralised root posture recorded.
6. **Apply baseline.** Delegated services, logging, security reporting, and required roles observed.
7. **Materialise workforce access.** The permission-set role exists and a human sign-in probe passes.
8. **Establish CI and workload trust.** Bounded tenant roles pass positive probes; excluded origins fail the negative ones.
9. **Join required regional networks.** Every required region shows its network cell, DNS, egress, and passing reachability claims.
10. **Prove audit and KMS paths.** Events arrive at the intended sink; key use succeeds while administration stays denied.
11. **Verify cost and effective quotas.** The cost owner is observed; every required quota is effective, not merely requested.
12. **Retire the bootstrap bridge.** Routine and emergency paths work without it; broad creator access removed or constrained.
13. **Publish the boundary.** The versioned `AssignedAccountRef` links account, access, network, quota, cost, and audit claims, each independently issued.

Failure handling inverts the {{project-shaped instinct | What a project-shaped platform learns: a half-built project can be destroyed and recreated on the next apply — the isolation unit is one more resource in the plan, so failure means rolling back to clean. | Project-shaped instinct}}. A partially provisioned account is never rolled back into non-existence: AWS accounts do not have a transactional lifecycle, and even closure is a durable state. A failed stage leaves the account in the catalogue — quarantined, observable, resumable from the stage that failed. The platform fails closed by withholding the reference, not by pretending the account never happened. Recovery stays narrow: creation retries only the failure reasons the docs mark transient, and a failed audit or KMS probe is classified first — eventual consistency still propagating, or a policy that is actually wrong. The first gets a retry with backoff; the second gets a fix; neither gets a weaker contract.

One consequence lands directly on IaC layout. The provider configuration for the new account needs an account ID and a role to assume, and neither exists until `CreateAccount` succeeds mid-apply. The apply that creates the account therefore cannot also deploy into it. The organisation lifecycle, the platform's member-account baseline, and the workload each keep their own stack and their own owner — the same downward dependency direction the model already required, now enforced by the tooling itself.

## Identity separates grants from limits

GCP flattens authorisation into one visible granting plane: a grant at the folder flows down the hierarchy, and reading the policy tree reads the access. AWS splits the same question into a granting plane (identity policies, resource policies, role trusts, {{KMS key policies | The mandatory policy document every key carries, and the key's root of authority: identity policies grant key access only where the key policy delegates to the account's IAM. GCP has no counterpart — a Cloud KMS key is governed by ordinary IAM bindings. | KMS key policy}}) and a limiting plane (SCPs, RCPs, permissions boundaries, session policies). The planes compose by intersection, with documented exceptions, and the exceptions are why probes beat policy reading.

The expensive translation error lives here. An SCP looks like the AWS spelling of a folder-level grant, and it is nothing of the kind: an SCP can make access impossible, and it cannot make access possible. A platform that ships guardrails believing it shipped access has built an account nobody can use — or one that works only through a leftover bootstrap role, which is worse. The binding rule already named the request, operations, surface, and holder for every shared mutation; AWS makes each of those a separate artefact and adds a plane that only subtracts.

### Five flows, one limiting plane

Each flow authenticates at a different place and carries its own proof. Permission sets materialise IAM roles in each account for people; everything else is a role with a trust to a specific authenticator. The emergency flow alone can fall back to root.

| Flow | Authenticates at | Materialised in the account | Granted by | Limited by |
| --- | --- | --- | --- | --- |
| Human workforce | Identity Center directory, Active Directory, or external IdP | `AWSReservedSSO_*` role from a permission-set assignment | Permission-set policies plus relevant resource and key policies | SCP/RCP, session duration, explicit deny |
| Workload runtime | AWS service identity or workload federation | Workload-owned role carrying the platform permissions boundary | Role policy plus resource and KMS policies | Trust conditions, constrained `iam:PassRole`, SCP/RCP, session policies |
| External CI / IaC | OIDC or SAML IdP | Bounded tenant deploy role with federated trust | Role policies plus cross-account target permissions | Provider-pinned issuer; audience and subject claims encoding repository, workflow, and ref; fork and PR exclusions; boundary |
| Cross-account platform | STS role assumption | Platform-owned role in the member account | Caller permission plus target trust and target-role policy | External-ID and source conditions, SCP/RCP, session policy |
| Break glass / root | Separately tested emergency identity; centralised root sessions for supported tasks | Time-bounded privileged session, or recovered root credential when unavoidable | Narrow emergency policy or root capability | Approval, monitoring, MFA where credentials exist, rapid removal |

The planes do not compose politely everywhere. Under the documented [evaluation logic](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_evaluation-logic.html), a same-account resource policy can grant directly to an IAM user or role-session principal around an implicit deny in a permissions boundary or session policy; explicit denies and applicable organisation controls still win. Protected resource policies therefore reject direct user and session principals unless the contract admits them, and the negative probes cover the permitted principal form.

KMS stays a separately owned capability rather than a sixth flow. Key administrators own policy changes, rotation, grants, and deletion; workload key users receive the required cryptographic operations and nothing else. A tenant role that can call `PutKeyPolicy` or `ScheduleKeyDeletion` owns the key, whatever the org chart says, and [cross-account use](https://docs.aws.amazon.com/kms/latest/developerguide/key-policy-modifying-external-accounts.html) needs permission on both the key policy and the caller's IAM path — so the probe has to run the real cross-account path.

> [!IMPORTANT]
> The management-account root keeps its own recovery contract: a dedicated protected mailbox, phishing-resistant MFA, no access keys, no routine use, monitored sign-in and recovery events, and independently authorised emergency access. [Centralised root access](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_root-enable-root-access.html) can remove member-account root credentials and run supported privileged tasks; it does not replace that contract.

### Bootstrap trust expires

The flow worth walking end to end is tenant CI — the pipeline a product team deploys its workload with. Every workload depends on it, and it is the flow bootstrap contaminates most easily: a pipeline that keeps deploying through the creation-time role never gets narrower. Trust is account-local. The OIDC provider and the bounded deploy role live inside the member account, and until the platform creates them the pipeline has nothing to authenticate with and nothing to assume. The account is born with the opposite already in place: a management-access role with authority over everything in it. A new account has exactly one usable path in, and it is the widest it will ever have. Bootstrap therefore has a direction: install the narrow paths, prove them, expire the broad one.

1. **Organizations creates the account and the broad role.** The platform records who created the account, the CloudTrail event that proves it, the account ID, and the role's name. That role is a skeleton key to everything in the account, so it goes on the books from its first second — a key that is not tracked cannot later be proved gone.
2. **A narrow platform session installs the steady state.** The steady state is every path that will outlive bootstrap: roles for the security and audit tooling, the Identity Center wiring for human sign-in, the OIDC trust tenant CI will deploy through, the network operator role, and the emergency controls. Each arrives as its own named installation, so each can be proved — and later withdrawn — on its own.
3. **Probes exercise every declared path, both ways.** What must work: the assigned human signs in, an admitted CI origin assumes its tenant role and nothing else, KMS use succeeds with both halves in place — the key policy and the caller's IAM. What must fail: forks, pull requests, excluded repositories, wrong refs, and wrong workflows assume nothing; protected resource policies turn away direct principals the contract never admitted; forbidden Organizations actions stay denied. A passing probe proves the path is reachable; only the failing probes prove the boundary.
4. **Broad bootstrap trust is removed or sharply constrained.** The order matters: routine and emergency paths have to work without the skeleton key before it is removed, so that removing it is not the step that locks the platform out. While that key still turns, the boundary cannot reach READY.
5. **Rotation and containment stay explicit.** There are no long-lived keys to rotate in this design — trust moves by changing the provider configuration. Containment is concrete: quarantine removes the assignments and the trust, applies deny controls, revokes sessions where AWS supports it, and records how long the credentials it cannot revoke will keep working.

```svg path="standalone/diagrams/02-tenant-ci-path.diagram.ts" width="560"
tenant CI run presents issuer, repository, ref, and workflow claims → the OIDC trust in the member account admits only matching origins; an excluded fork, pull request, ref, or workflow terminates with no session → the bounded tenant deploy role is the granting plane → the limiting plane of SCPs, RCPs, the permissions boundary, and session policies subtracts from that grant (an SCP grants nothing) → what survives is the admitted account-local surface.
```

## Networks are regional before they are shared

A GCP VPC is global with regional subnets; an AWS VPC is regional with zonal subnets. That one line moves region out of implementation detail and into contract context. A second region is not another subnet in the same network — it is another admitted cell, with its own VPC or share, DNS association, egress posture, and failure domain. [The `NetworkRef`](/blog/making-iac-boring/02-contract) survives; its cardinality changes. The assigned boundary carries one per required region, and the reference is withheld until all of them hold.

"Shared VPC becomes the networking account" is the shortcut that fails: naming an account decides nothing. The operating model is who owns routes, DNS, endpoints, and egress; which mutations application accounts are admitted to make; and how a participant leaves.

### Two ownership profiles

The profile choice is an ownership decision before it is a topology one. Shared subnets keep the strongest Shared VPC resemblance and the strongest coupling: participants [inherit the owner's operational decisions](https://docs.aws.amazon.com/vpc/latest/userguide/vpc-share-limitations.html), and exit is two-stage by construction, because unsharing stops new placement without removing what participants already built. Per-account VPCs trade the noun resemblance for an account-owned lifecycle, with the platform owning transit, shared DNS, and the IP plan. [Both patterns are documented AWS practice](https://docs.aws.amazon.com/whitepapers/latest/building-scalable-secure-multi-vpc-network-infrastructure/welcome.html); neither is a universal default.

| Question | Shared subnets via RAM | Per-account VPC plus transit |
| ---- | --------- | --------- |
| What it is | The network account runs one VPC per region and shares subnets into application accounts; workloads deploy onto a network someone else operates | Every application account runs its own regional VPC; the platform connects them through Transit Gateway only where reachability is earned |
| What the network account owns | Everything about the network: the VPC, subnets, routes, NACLs, NAT, endpoints, Resolver endpoints, and the Transit Gateway attachment | The connective tissue: transit route domains, shared DNS, the IP plan, and optional central egress and inspection |
| What the application account owns | Only what sits on the shared subnets: workload resources, ENIs, security groups, and flow logs for its own ENIs | Its whole VPC — subnets, routes, security groups, endpoints — inside the platform contract |
| Who can break whom | A route or NAT change by the owner lands on every participant at once | Transit and shared-service changes still fail wide; VPC-local changes stay inside one account |
| How a region joins | Choose which shared network the account belongs in, share the subnet, observe the association, prove placement, DNS, and egress, then publish the regional `NetworkRef` | Allocate a CIDR, create the VPC, attach transit, associate the route domain, wire DNS and egress, probe, publish |
| How it tears down | The share is revoked first, which only blocks new placement; participants drain what they built; the owner surfaces come down last | Withdraw admission, drain, detach DNS, endpoints, transit, and IPAM, then delete the VPC once recorded consumers reach zero |
| When it fits | Participants accept central ownership and common fate: sandboxes, centrally operated platforms | Accounts need an independent lifecycle or their own regional policy |

### The admission contract replaces the bag of IDs

A project-shaped handoff passes a bag of raw VPC and subnet IDs and lets the consumer infer the rest. The {{admission contract | The published agreement under which an application account joins a regional network cell: what the account may place and change there, what the platform has proved about the cell, and how the account leaves. | Admission contract}} replaces the bag with claims a reviewer can read. It names where the account landed: the region, the topology class, the VPC or route-domain identity, the admitted subnets or placement class. It names how traffic is meant to behave: the intended reachability, the DNS path, the egress and endpoint policy actually selected. It names who governs the edges: attachment authority, the readiness evidence behind each claim, and the detachment procedure for leaving. An `AssignedAccountRef` carries one `NetworkRef` for every required region; a region added later advances the profile generation instead of weakening the existing cells.

Workload code declares connectivity intent; the network owner applies changes to shared routes, DNS, endpoints, inspection, and egress. Organisation and account-local controls block unapproved internet gateways, peerings, transit attachments, and shared-route mutations, and reconciliation reports every route or association outside the published contract. That split is the model's binding rule applied to a regional surface.

IPAM, central inspection, shared endpoints, Transit Gateway, and Cloud WAN stay conditional capabilities. Transit is not a default: an isolated account needs no attachment, cross-VPC or on-premises reachability earns a Transit Gateway, and global segmentation has to earn Cloud WAN separately.

## A layout that keeps the owners apart

The tree that satisfies every section above is ordinary:

```tree
AWS Organizations
├─ management account
│  # irreducible Organizations · billing · account lifecycle
├─ Security OU
│  ├─ security tooling account
│  │  # delegated security services · findings
│  └─ log archive account
│     # organization trails · immutable retention
├─ Infrastructure OU
│  ├─ network account
│  │  # transit · shared DNS · optional egress/inspection
│  ├─ platform account
│  │  # boundary coordinator · catalogue · CI
│  └─ shared-services account
│     # optional common registries/endpoints
├─ Workloads OU
│  ├─ product-a-nonprod
│  │  # workload state · bounded runtime roles · regional VPC
│  ├─ product-a-prod
│  │  # workload state · bounded runtime roles · regional VPC
│  └─ ...
├─ Sandbox OU
│  # reduced account product · bounded connectivity
└─ Quarantine / Suspended OU
   # failed onboarding or retirement isolation
```

> [!NOTE]
> The tree is my opinionated starting point, not an AWS requirement. AWS's whitepaper nests Prod and Test child OUs under Workloads — flat sibling accounts hold until per-environment policies diverge. Policy grouping, account lifecycle, and workload runtime stay separately owned whatever the spelling; an existing Control Tower estate keeps its Foundational OU conventions instead.

The management account stays thin because nothing constrains it: SCPs do not apply to it, and [AWS's own guidance](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_best-practices_mgmt-acct.html) is to use it only for tasks that require it. Irreducible Organizations, billing, and account-lifecycle operations stay; metadata, security tooling, log archive, and baseline execution sit behind delegated administrators and member-account roles. Delegation is uneven across services, so the small set of management-account automation that remains is recorded as such rather than wished away.

The two-party split from the vending section sharpens into eight boundaries: what each owns, what it publishes, and what it must never own. As with the stage owners, eight boundaries are not eight teams — all eight can sit with one person, and the table still names which responsibility is in play when a change lands.

| Boundary | Owns | Publishes | Must not own |
| --- | ------ | ------ | ----- |
| Organisation lifecycle | Management-account executor, root, OUs, accounts, policy attachments | Authorised account-operation receipts and observed lifecycle | Application resources or tenant release state |
| Boundary coordinator | Request state, product version, receipt validation, quarantine and retirement coordination | `AssignedAccountRef` linking separately owned claims | Underlying IAM, network, audit, or account mutations; self-approved evidence |
| Identity platform | Identity source, groups, permission sets, assignments, emergency policy | Human-access and platform-role readiness | Workload runtime policies |
| Security and audit | Delegated services, organization trails, findings, log retention, root posture | Enrolment, reporting, and containment readiness | Ordinary workload deployment |
| Network platform | IP plan, optional transit, shared DNS, admission, optional egress and inspection | Regional `NetworkRef` and detach receipts | Application ENIs, runtime security groups, workload services |
| FinOps platform | Cost-identity mapping, quota requests and verification, from the platform account | Cost-owner and effective-quota claims | Workload spend decisions or release state |
| Workload platform stack | Bounded tenant CI role, platform permissions boundary, approved VPC-local surfaces | Stable inputs for workload IaC | Organizations mutations, protected principals, shared transit or DNS changes |
| Workload stack | Services, data, bounded runtime roles, workload policies, records on delegated surfaces | Backends and endpoints for admitted consumers | Account creation, OU movement, shared routing, federation, audit principals |

### The platform applies in order

1. **Organisation stack.** Organizations itself, OUs, baseline policies, integrated services, delegated administrators, the root and contact namespace.
2. **Account lifecycle executor.** A minimal management-account path that creates and observes the member account, moves it, and returns signed operation receipts. Control Tower remains a separate selected implementation, not a pre-built adapter.
3. **Security, audit, and identity stacks.** Enrol services, create steady-state roles and assignments, prove reporting and human and platform access.
4. **Regional network publishers.** Create or admit each required cell, adding transit, DNS, endpoints, or egress only when the boundary product selects them, then prove declared reachability.
5. **Bootstrap handoff.** Prove tenant CI and emergency paths, then remove or narrow broad creator trust.
6. **Boundary publication.** The coordinator validates account, access, network, quota, cost, audit, and proof-expiry claims before publishing.
7. **Workload release.** Resolves the boundary reference and stays inside the admitted account-local surfaces.

### Drift has classes

Capability owners keep observing their claims after publication, and the coordinator keeps evaluating receipts. **Blocking** drift prevents first publication. **Security-critical** drift withdraws the affected capability, blocks new releases and role assumptions, applies containment, and quarantines the boundary until containment evidence holds. **Repairable** drift reconciles behind the published contract only while every published claim stays true; a stale or failed proof withdraws the claim. **Accepted** drift advances the desired profile through review. Control Tower's drift detection is one signal inside this model, not the model.

## Failure modes, owners, containment

The machinery above fails in ordinary ways, and each failure already has an owner. The rows follow the boundary's own path — request, creation, placement, access, networks, audit and KMS, bootstrap retirement, closure — so every failure sits beside the stage that produces it. Each row answers the same three questions: what notices the failure, who owns the recovery, and how the platform fails closed while it is broken. Across all of them one rule holds: no recovery widens a trust, a route, or a policy to get unstuck.

| Failure | Detected by | Recovery owner | Fail-closed behaviour |
| --- | --- | --- | --- |
| Unauthorised boundary transition | Caller-to-owner binding, independent approval receipt, immutable audit event | Platform and security | Reject before provider calls; the coordinator cannot approve its own request |
| Account creation stalls | Creation-status polling, CloudTrail, timeout SLO | Organisation lifecycle | No reference; the request stays retryable or terminal with evidence |
| Wrong OU or policy inheritance | Observed parent and inherited-controls enumeration | Organisation lifecycle | No publication; quarantine intent until corrected |
| Permission set exists, role unusable | Provisioned-role observation plus an STS or console probe | Identity platform | No IAM-user fallback; the separately tested emergency path stands |
| SCP mistaken for a grant | Grant-versus-limit labelling in the access profile; the positive probe fails | Identity and security | Refuse readiness with the missing grant or trust named |
| Tenant role exceeds its boundary | Boundary conformance, `iam:PassRole` inventory, protected-principal drift | Identity and platform | Block the change or assumption; quarantine the tenant deploy path |
| Transit route or DNS association missing | Reachability and resolution probes from both sides | Network platform | The network claim stays unready; workload apply does not start |
| Workload creates an unadmitted path | Route, peering, gateway, and endpoint reconciliation | Network and workload platform | Deny where possible; withdraw the network claim until the path is removed |
| Shared subnet unshared with residual resources | Share state plus inventory of participant-owned ENIs | Network owner and participant | Stop placement, drain, then remove the owner surface |
| KMS use or administration mismatch | Key-policy inspection, constrained use probe, grant checks | Key administrator and key user | Refuse the capability; tenant roles cannot mutate policy or schedule deletion |
| Audit events miss the archive | Canary event and sink observation | Security and audit | The account is not ready for governed workloads |
| Bootstrap trust cannot be retired | Steady-state access proof and trust inventory | Platform and security | Account stays quarantined; recovery exceptions never satisfy READY |
| Closure requested with live consumers | Recorded consumers reconciled against provider-side relationships | Coordinator and capability owners | Unexplained relationships block zero-consumer proof and closure |

## READY states exactly what holds

The assigned reference on AWS says more than its GCP ancestor because it has to. It names the account, the access paths that were proved, the regional network cells that were admitted, the effective quotas, the cost owner, the audit sink observed receiving events, and the contract version all of it was proved against. It also names what was not proved, when the proof expires, and which failure withdraws it. READY is bounded assurance for listed claims, not an explanation of every AWS authorisation decision; nobody can honestly sell the latter.

A product team can preview its workload when the reference resolves and every claim it names is current at once; a finished apply proves one publisher's claim and nothing above it.

```svg path="standalone/diagrams/03-assigned-account-ref.diagram.ts" width="600"
One AssignedAccountRef names the account, contract version, and proof expiry, and links — never embeds — the separately owned claims: an AccessRef from the identity platform, one NetworkRef per required region from the network platform, and the audit, cost, and quota claims, each carrying its own version.
```

Removal keeps the model's order: vacate first, then destroy. AWS adds its own weight. Retirement marks the boundary RETIRING and blocks new admission while existing references stay resolvable for teardown; consumers detach their cross-account edges; closure is requested only when the recorded reverse edges reach zero. Then the account still does not disappear — [closure keeps a 90-day reopen window and rolling closure quotas](https://docs.aws.amazon.com/organizations/latest/APIReference/API_CloseAccount.html). Even destruction is an observed lifecycle, which is the claim this article has made about every other boundary.

## Rejected defaults, and what would overturn them

| Alternative | Why rejected here | What would overturn it |
| --- | --- | --- |
| Control Tower as the default | Adds enrolment, baselines, Service Catalog, controls, and a shared-account lifecycle before a need exists; direct Organizations still owns its own workflow, evidence, and staffing | An existing Control Tower estate, mandated managed controls, a regulated evidence regime, or custom ownership cost approaching the managed lifecycle's |
| AFT as the default | Depends on Control Tower and adds a GitOps workflow, repositories, pipelines, and state | A real fleet of repeatable account customisations already on Control Tower |
| Shared subnets everywhere | Central control and residual participant resources weaken independent workload lifecycle | A dense, centrally operated common-trust domain |
| Per-account VPC everywhere | DNS, IP allocation, endpoints, and cost can outweigh isolation for sandboxes and shared platforms | Independent lifecycle earns the VPC profile; reachability separately earns transit |
| Cloud WAN | No established global policy or segment scale to justify another control plane | Multi-region, multi-site segmentation beyond Transit Gateway's manageable scope |

The rest do not trade off against a baseline; they violate the model itself, and no condition overturns them. The first table's defaults return once the estate or the scale changes; these return only if the model is wrong.

| Alternative | Why it violates the model |
| --- | --- |
| One apply creates account and workload | Creation and readiness are asynchronous and cross several owners; tooling can orchestrate stages, but the boundary remains |
| Workload creates its own account | Reverses the dependency graph and hands tenant automation organisation authority — no credible normal case |
| Permanent `OrganizationAccountAccessRole` as deploy path | Conflates bootstrap with steady-state deployment and keeps fleet-wide blast radius; recovery admits only a temporary, owned, audited exception — never workload pipelines |
| SCPs as access policy | SCPs limit the maximum permission set; they grant nothing, skip the management account, and stay guardrails beside real grants |
| One mega-orchestrator owns every transition | Account, identity, network, audit, and teardown have different owners and cadences; owners publish receipts, the coordinator validates their composition |

Five decisions stay with each estate, because no documentation can settle them: whether Control Tower is already deployed and brownfield accounts must be enrolled rather than rebuilt; how many accounts and regions are coming, and whether a managed control catalogue is mandated; which identity provider is authoritative on day one; which workloads actually need cross-boundary connectivity, inspection, or global segmentation; and which data, logs, and keys must outlive the account, and who owns the retained copies. None of them blocks the protocol; each can overturn an implementation choice behind it.

The port added no new rule; it added receipts — the work moves from designing the handoff to proving each stage of it. A platform that runs this model on one cloud already holds it for the other; what it does not yet hold is the evidence the second cloud refuses to waive.
