The Multi-Account Journey - Why Structure Matters

The Multi-Account Journey - Why Structure Matters architecture diagram
Click to expand
832 × 583px

Many organisations start with a single AWS account. Development resources, production workloads, DNS zones, billing - all jumbled together. It works, until it doesn't.

This series documents how we restructured a client's AWS presence into a proper multi-account setup, managed entirely through infrastructure as code. The business goal was clear: deliver a production AWS-hosted website with strong security boundaries and repeatable deployment workflows.

The Problem with One Account

A single AWS account seems simpler. One login, one bill, one place for everything. But the cracks appear quickly:

  • Blast radius: A misconfigured IAM policy affects everything
  • Cost attribution: Which project is burning through that compute budget?
  • Access control: How do you give a contractor DNS access without exposing production databases?
  • Compliance boundaries: Some workloads need stricter controls than others

These problems compound quickly. The final straw is often realising you can't safely give CI/CD pipelines the permissions they need without also giving them the keys to the kingdom.

The Target Architecture

The solution is AWS Organizations with purpose-built accounts:

aws-account-structure-part-1-the-journey/account-structure diagram

Three accounts, three purposes:

AccountPrimary responsibilitiesWhy separate
Management AccountAWS Organizations, IAM Identity Center, CloudTrail, shared Terraform stateKeeps org-level control plane isolated from application risk
DNS AccountRoute 53 hosted zones and DNS-related infrastructureDNS mistakes can break everything; isolate the blast radius
Website Production AccountAWS Amplify apps and website workloadsSeparates production runtime and cost from shared services

This isn't the only valid structure - many organisations have dozens of accounts for different environments and teams (AWS's multi-account strategy whitepaper covers these patterns in depth). But for a small-to-medium operation, three accounts hits the sweet spot between isolation and complexity.

Three Repositories, One System

Managing this setup requires infrastructure as code. We split the configuration across three repositories, each with a specific responsibility:

aws-bootstrap

The foundation layer. This repository solves the chicken-and-egg problems of AWS setup:

  • Enables AWS Organizations (requires manual console step first)
  • Creates CloudTrail for audit logging
  • Sets up AWS Config for compliance monitoring
  • Provisions the S3 bucket and DynamoDB table for Terraform state
  • Configures GitHub Actions OIDC for keyless CI/CD

You run this once to establish the baseline, then rarely touch it again.

aws-identity-management

The identity layer. Everything related to who can access what:

  • IAM Identity Center users and groups
  • Permission sets defining access levels
  • Account assignments linking permissions to accounts
  • Cross-account deployment roles
  • Service Control Policies for guardrails

This is where the complexity lives. Permission sets, session durations, least-privilege policies - all managed as code.

aws-dns-management

The DNS layer. Route 53 zones and records:

  • Hosted zones for each domain
  • MX records for email services
  • TXT records for verification and SPF
  • CNAME records for service endpoints
  • A records for websites

Keeping DNS separate means a bad deployment to production can't accidentally delete your MX records.

Why OpenTofu?

All three repositories use OpenTofu rather than Terraform. The practical differences are minimal - it's a fork that maintains compatibility with Terraform providers and modules. We chose it for the open-source licensing, but the code would work with Terraform too.

The key infrastructure patterns work regardless of which tool you use:

  • S3 backend for remote state
  • DynamoDB for state locking
  • GitHub Actions for CI/CD
  • OIDC for keyless authentication

The Deployment Order

These repositories have dependencies. You can't manage Identity Center users before Organizations exists. You can't use the S3 backend before the bucket exists.

The deployment sequence:

  1. Bootstrap (first run): Run without remote backend (tofu init -backend=false, then tofu apply) so state is created locally. Use a break-glass admin path for initial org bootstrap; avoid root except where AWS explicitly requires it.
  2. Bootstrap (second run): Enable the backend and migrate local state into S3/DynamoDB with tofu init -migrate-state
  3. Identity Management: Depends on bootstrap outputs for state backend config
  4. DNS Management: Depends on identity management for cross-account roles

After initial setup, all changes flow through GitHub Actions. Push to main, review the plan, approve the apply.

For teams moving from a single AWS account to this model, the hard part is usually the transition plan, not the target architecture. This is the kind of delivery Octasoft typically supports:

Focus areaPractical outcome
Account boundary designClear split of control plane, workloads, and shared services
Migration sequencingLow-risk cutover path that avoids "big bang" moves
CI/CD and role hardeningShort-lived credentials and scoped cross-account deployment roles
Operating modelRunbooks for break-glass access, incident response, and day-2 changes

Is This Overkill?

For a solo developer or small team, this might seem like unnecessary complexity. And honestly, if you're just experimenting with AWS, a single account is fine.

The common path looks like this:

  • Month 1: one account, one pipeline, fast progress.
  • Month 6: production traffic grows, more people need access, and "temporary" IAM permissions become permanent.
  • Month 12: DNS, runtime workloads, CI/CD trust, and shared state are all intertwined. Any cleanup now is high-risk because you're refactoring the plane while flying it.

That's why early planning matters. You don't need a huge enterprise design on day one, but you do want a few durable boundaries before sprawl sets in: management as control plane, workloads in their own accounts, and infrastructure access through short-lived roles instead of static keys.

If you're running production workloads, handling customer data, or working with a team, that investment pays off quickly. More importantly, everything is code. You can rebuild the entire setup from scratch by running three tofu apply commands. That's the real value.

What's Coming

Over the remaining posts, we'll walk through each component in detail:

  • Part 2: AWS Organizations - the foundation everything else builds on
  • Part 3: Security foundations - CloudTrail, Config, and password policies
  • Part 4: Terraform state management - solving the bootstrap problem
  • Part 5: GitHub Actions OIDC - eliminating long-lived credentials
  • Part 6: IAM Identity Center - why it replaces IAM users
  • Part 7: Permission sets - designing access patterns
  • Part 8: Cross-account access - deployment roles and role chaining
  • Part 9: Service Control Policies - organizational guardrails
  • Part 10: DNS management - Route 53 at scale
  • Part 11: Website hosting - AWS Amplify deployment model and customer runbook

Each post includes the actual OpenTofu code (sanitised of account-specific details) and explains the reasoning behind each choice.


This is Part 1 of the AWS Account Structure Series.

← Back to all posts