Multi-Account Cloud Architecture with Terraform#
Single-account cloud deployments work for learning and prototypes. Production systems need multiple accounts (AWS), subscriptions (Azure), or projects (GCP) for isolation — security boundaries, blast radius control, billing separation, and compliance requirements.
Terraform manages multi-account architectures well, but the patterns differ significantly from single-account work. Provider configuration, state isolation, cross-account references, and IAM trust relationships all need explicit design.
Why Multiple Accounts#
| Reason | Single Account Problem | Multi-Account Solution |
|---|---|---|
| Blast radius | Misconfigured IAM affects everything | Damage limited to one account |
| Billing | Cannot attribute costs to teams | Per-account billing and budgets |
| Compliance | PCI data mixed with dev workloads | Separate accounts for regulated workloads |
| Service limits | VPC limit of 5 per region shared | Each account has its own limits |
| Access control | Complex IAM policies to isolate teams | Account boundary is the strongest isolation |
| Testing | Dev resources can affect production | Impossible for dev to touch prod resources |
AWS Organizations#
Organization Structure#
Organization Root
├── Core OU
│ ├── Management Account (billing, org management)
│ ├── Security Account (GuardDuty, SecurityHub, audit logs)
│ └── Networking Account (Transit Gateway, shared VPCs)
├── Workload OU
│ ├── Production OU
│ │ ├── App-A Production Account
│ │ └── App-B Production Account
│ └── Non-Production OU
│ ├── App-A Development Account
│ └── App-A Staging Account
└── Sandbox OU
└── Developer Sandbox AccountsTerraform for AWS Organizations#
resource "aws_organizations_organization" "main" {
feature_set = "ALL"
enabled_policy_types = [
"SERVICE_CONTROL_POLICY",
"TAG_POLICY",
]
}
resource "aws_organizations_organizational_unit" "core" {
name = "Core"
parent_id = aws_organizations_organization.main.roots[0].id
}
resource "aws_organizations_organizational_unit" "workloads" {
name = "Workloads"
parent_id = aws_organizations_organization.main.roots[0].id
}
resource "aws_organizations_organizational_unit" "production" {
name = "Production"
parent_id = aws_organizations_organizational_unit.workloads.id
}
# Create a workload account
resource "aws_organizations_account" "app_production" {
name = "app-a-production"
email = "aws+app-a-prod@example.com"
parent_id = aws_organizations_organizational_unit.production.id
role_name = "OrganizationAccountAccessRole" # cross-account admin role
lifecycle {
prevent_destroy = true # accounts cannot be easily recreated
}
}Service Control Policies (SCPs)#
SCPs set permission boundaries for entire OUs: