Structure from day one#

Most teams start on AWS the same way: one account, signed in as the root user, with test, staging and production all deployed side by side. That works until a test script deletes the wrong resource, the bill can't tell you which environment spent what, or one leaked credential exposes everything at once.

The alternative is to design a secure, workable structure up front. On AWS that means AWS Organizations, a no-cost service that groups multiple accounts under central management with consolidated billing and centrally applied policies.

This guide covers how to design that structure in general terms: what each piece of AWS Organizations is, how to split accounts by environment and by workload, how to create a production OU and account step by step in the native AWS console, how to automate it with the AWS CLI and Terraform, and which guardrails, access controls and billing controls to switch on from the start.

Documentation: AWS · What is AWS Organizations ↗ · AWS · Organizing your AWS environment (whitepaper) ↗

Four concepts you need to keep apart#

The most common vocabulary mistake (the original version of this article made it too) is calling every folder an organization. AWS Organizations has distinct pieces:

  • Organization: the whole entity. There is one per management account, and it contains every account.
  • Management account: the account that created the organization. It pays the consolidated bill, creates and invites accounts, and manages policies. It is your most sensitive account.
  • Root: the top container of the hierarchy. Every OU and account hangs off it, directly or indirectly.
  • Organizational unit (OU): a folder under Root (or under another OU) that groups accounts sharing the same policies, such as production or sandbox.
  • Member account: a regular AWS account that belongs to the organization. It is the real isolation boundary for resources, permissions and quotas.

The key idea: the account is the security and billing boundary; the OU is how you apply rules to a group of accounts at once.

Documentation: AWS · What is AWS Organizations ↗

Step 1: open AWS Organizations#

Sign in to the console as a user of the account that will become the management account. Type Organizations in the top search bar and select AWS Organizations.

You'll land in one of two situations:

  • If you have never created an organization, the console shows a Create an organization button. Clicking it turns your current account into the management account and creates Root.
  • If an organization already exists, you go straight to the structure view (AWS accounts), where you create accounts and OUs.

The account that creates the organization stays its management account. If that account already runs production workloads, plan to move them into a member account over time rather than keep building there.

Documentation: AWS · What is AWS Organizations ↗ · AWS · Best practices for the management account ↗

Step 2: create the production organizational unit#

In the structure view, select Root, which will be the parent of the new OU, then choose Organizational unit > Create new.

In the form, type production in the Organizational unit name field and click Create organizational unit.

Back in the tree you'll notice two things:

  • The production OU sits under Root, exactly where you created it.
  • The OU is empty: it contains no accounts and no other OUs yet.

An empty OU costs nothing and does nothing on its own. It becomes useful once you place accounts and attach policies to it.

Documentation: AWS · Creating an organizational unit ↗

Step 3: create the production AWS account#

Now create the account that will live in that OU. Click Add an AWS account and keep the default option, Create an AWS account. Fill in the form:

  • AWS account name: a practical choice is the OU's name or a workload-environment pattern (for example production or shop-prod). It's just a label and you can change it later.
  • Email address of the account's owner: the root user email for the new account. It must be valid and not already used by another AWS account. An alias works; Gmail and many providers accept plus addresses such as [email protected]. A team distribution list is better than one person's inbox.
  • IAM role name: leave the default, OrganizationAccountAccessRole. Organizations creates this role in the new account with administrator permissions and a trust relationship to the management account, so you can get in without root credentials.

Click Create AWS account. Creation is asynchronous and can take a few minutes; the console shows a notification and you can follow progress with View all pending creation requests.

When it finishes, the account appears in the tree at the first level, directly under Root. That is expected: accounts created from the console start in Root.

Documentation: AWS · Creating a member account ↗ · AWS · Accessing member accounts with OrganizationAccountAccessRole ↗

Step 4: move the account into the right OU#

For the account to inherit the production OU's policies, move it out of Root:

  • Tick the box next to the production account in the tree.
  • Choose Actions and, under AWS account, select Move.
  • Pick the destination OU, production, and click Move AWS account.

Expand the production OU to confirm the account is now inside it. This also shows that the hierarchy isn't set in stone: you can move accounts between OUs later if you reorganize. Keep in mind that a moved account stops inheriting the old OU's policies and starts inheriting the new one's, so review the SCPs before moving anything that runs production.

Documentation: AWS · Moving accounts between OUs ↗

Automate it: AWS CLI and Terraform#

The console is the best way to understand the flow. To repeat it without mistakes (another account for staging, another for sandbox), keep it as code. Here are the same steps with the AWS CLI:

bash
# Run with management account credentials
ROOT_ID=$(aws organizations list-roots --query 'Roots[0].Id' --output text)

# 1. Create the "production" OU under Root
OU_ID=$(aws organizations create-organizational-unit \
  --parent-id "$ROOT_ID" --name production \
  --query 'OrganizationalUnit.Id' --output text)

# 2. Create the member account (asynchronous: returns a request id)
REQ_ID=$(aws organizations create-account \
  --account-name production \
  --email [email protected] \
  --role-name OrganizationAccountAccessRole \
  --query 'CreateAccountStatus.Id' --output text)

# 3. Poll the status until it is SUCCEEDED
aws organizations describe-create-account-status \
  --create-account-request-id "$REQ_ID"

# 4. Move the account from Root into the production OU
aws organizations move-account --account-id <ACCOUNT_ID> \
  --source-parent-id "$ROOT_ID" --destination-parent-id "$OU_ID"
The same four steps with the AWS CLI. create-account is asynchronous: wait for SUCCEEDED before moving the account. Replace the email and account ID with your own.

With Terraform you declare the OU and the account, and create the account directly inside the OU using parent_id, which removes the move step entirely:

hcl
data "aws_organizations_organization" "this" {}

resource "aws_organizations_organizational_unit" "production" {
  name      = "production"
  parent_id = data.aws_organizations_organization.this.roots[0].id
}

resource "aws_organizations_account" "production" {
  name      = "production"
  email     = "[email protected]"
  role_name = "OrganizationAccountAccessRole"
  # Created directly inside the OU: no move step needed
  parent_id = aws_organizations_organizational_unit.production.id

  lifecycle {
    ignore_changes = [role_name]
  }
}
Illustrative example using the AWS provider for Terraform/OpenTofu, run with management account credentials. Check the resource docs for what happens on destroy before you apply it.

Documentation: Terraform AWS provider · aws_organizations_organizational_unit ↗ · Terraform AWS provider · aws_organizations_account ↗ · AWS · Creating a member account ↗

Guardrails: SCPs, access and the management account#

Separate accounts are the foundation; guardrails are what make the structure secure.

Service Control Policies (SCPs). You attach them to Root, an OU or an account, and they set the maximum permissions available in the affected accounts. They never grant permissions by themselves; they cap what IAM can grant. One important detail: SCPs do not affect users or roles in the management account, which is one more reason to keep workloads out of it.

json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyLeavingTheOrganization",
      "Effect": "Deny",
      "Action": "organizations:LeaveOrganization",
      "Resource": "*"
    }
  ]
}
A minimal SCP for the production OU that stops member accounts from leaving the organization. Test it in a staging OU first: a badly written SCP can block legitimate operations.

Access. Instead of sharing root credentials or creating IAM users in every account, use IAM Identity Center to give each person scoped access to only the accounts they need. Protect every account's root user with MFA and keep it out of day-to-day work.

Access. Instead of sharing root credentials or creating IAM users in every account, use IAM Identity Center (covered in the next section). Protect every account's root user with MFA and keep it out of day-to-day work.

Documentation: AWS · Service Control Policies (SCPs) ↗ · AWS · What is IAM Identity Center ↗ · AWS · Best practices for the management account ↗ · AWS Well-Architected · Security pillar ↗ · AWS · Example SCPs ↗

Access with IAM Identity Center#

With many accounts, creating IAM users in each one quickly becomes unmanageable. IAM Identity Center centralizes access: you connect an identity source (its built-in directory, Active Directory or an external provider such as Okta or Microsoft Entra ID), define permission sets and assign groups to specific accounts. Everyone signs in through a single portal and gets temporary credentials for the account and role they need.

  • Enable IAM Identity Center from the organization's management account and choose the identity source.
  • Consider delegating Identity Center administration to a member account so day-to-day work doesn't require signing in to the management account.
  • Create permission sets per job function (for example AdministratorAccess for the platform team, ReadOnly for production lookups) rather than per person.
  • Assign groups, not individual users, to each account with the right permission set: broad access in sandbox and staging, narrow access in production.
hcl
data "aws_ssoadmin_instances" "this" {}

locals {
  sso_instance_arn = tolist(data.aws_ssoadmin_instances.this.arns)[0]
}

# Permission set: a reusable bundle of permissions for many accounts
resource "aws_ssoadmin_permission_set" "read_only" {
  name             = "ReadOnly"
  instance_arn     = local.sso_instance_arn
  session_duration = "PT4H"
}

resource "aws_ssoadmin_managed_policy_attachment" "read_only" {
  instance_arn       = local.sso_instance_arn
  permission_set_arn = aws_ssoadmin_permission_set.read_only.arn
  managed_policy_arn = "arn:aws:iam::aws:policy/ReadOnlyAccess"
}

# Assignment: the "developers" group gets read-only access to production
resource "aws_ssoadmin_account_assignment" "developers_prod" {
  instance_arn       = local.sso_instance_arn
  permission_set_arn = aws_ssoadmin_permission_set.read_only.arn
  principal_type     = "GROUP"
  principal_id       = var.developers_group_id
  target_type        = "AWS_ACCOUNT"
  target_id          = aws_organizations_account.production.id
}
Illustrative Terraform/OpenTofu: a read-only permission set assigned to a group in the production account. var.developers_group_id is the group's ID in the identity store.

Documentation: AWS · Getting started with IAM Identity Center ↗ · AWS · Permission sets ↗ · AWS · Delegated administration for IAM Identity Center ↗ · Terraform AWS provider · aws_ssoadmin_permission_set ↗ · Terraform AWS provider · aws_ssoadmin_account_assignment ↗

Billing: one bill, costs per account#

Organizations turns on consolidated billing: the management account pays a single bill that adds up usage from every member account. That is the practical payoff of splitting by account: the cost of each environment and workload shows up broken down without having to tag every resource perfectly.

  • Group by linked account in Cost Explorer to see spend per environment and application.
  • Activate cost allocation tags (for example team or project) from the management account to break down costs inside a shared account. Tags only show up in reports after activation.
  • Create per-account budgets with AWS Budgets, especially for sandbox, with alerts before the limit is reached.
  • By default, Reserved Instance and Savings Plans discounts are shared across the organization's accounts; you can turn off sharing per account if the savings must be attributed to a specific team.

Documentation: AWS · Consolidated billing ↗ · AWS · Cost allocation tags ↗ · AWS · Managing costs with AWS Budgets ↗ · AWS · Turning off RI and Savings Plans discount sharing ↗

Structure checklist#

  • The management account runs no workloads.
  • Production lives in its own account inside a production OU.
  • Staging, QA and sandbox sit in accounts and OUs separate from production.
  • Each account uses a valid, unique team email and its root user has MFA.
  • People reach accounts through IAM Identity Center or OrganizationAccountAccessRole, never shared root credentials.
  • At least one baseline SCP is attached, and it was tested in a non-production OU first.
  • The structure lives in code (CLI or Terraform) so you can repeat it.
  • Each workload has its own account per environment, or the design lets you split it later without rebuilding.
  • Per-account budgets exist and the cost allocation tags you rely on are activated.

With structure designed in from the start, adding a new environment stops being a project and becomes a small change. It is a foundation that scales as teams and projects grow.

Documentation: AWS · Recommended OUs and accounts ↗

Sources and scope

Documentation checked on September 25, 2026. Examples and decision criteria are editorial proposals; adapt them to your application's contract and validate them in an authorized test environment.

From design to decision

Compare cloud options

Review pricing, limits, conditions and sources for each option (in Spanish).

Open comparison