Skip to main content

4. AWS organisation

What it is​

Instead of one AWS account for everything, we create a small organisation of accounts: one management account that only handles billing and sign-in, and one member account per environment (dev, staging, prod) plus a couple of support accounts. People sign in once through IAM Identity Center (AWS's single sign-on) and pick the account and role they need.

The design reasons are in AWS accounts and environments. This page is the exact setup.

Why we do it​

  • Blast radius. A mistake in dev cannot delete prod, because prod is a different account.
  • Permissions. A new developer can have full access to dev and read-only access to prod.
  • Cost. Each account's bill is separate, so you see what dev costs without tagging.
  • Handover. Each member account can be moved into the client's own organisation later.

Before you start​

  • Team mailboxes, for example aws-root@example.com. Most mail systems allow aws-root+dev@example.com style aliases; each AWS account needs a unique address.
  • A company card for the management account (it pays for all accounts).
  • The main region, agreed with the project lead. For India this is usually ap-south-1 (Mumbai). Some global services also need us-east-1.
  • AWS CLI v2: brew install awscli.

How it fits together​

An OU (organisational unit) is a folder of accounts. Rules attached to an OU apply to every account in it.

Steps​

1. Create the management account​

  1. Go to aws.amazon.com and choose Create an AWS Account.
  2. Root email: aws-root@example.com. Account name: example-management.
  3. Add the company card. Choose the Basic support plan for now.
  4. Sign in as root. Open the account menu (top right), Security credentials, Assign MFA device. Use an authenticator app. Store the root password and the MFA recovery in the team vault.
  5. In the account menu, Account, turn on IAM user and role access to Billing information, so SSO admins can see the bill.

The root user is now put away. After step 4 you only use it for the few tasks that require it.

2. Set a budget alarm​

Billing and Cost Management, Budgets, Create budget, Monthly cost budget. Set an amount agreed with the project lead and email alerts at 80% and 100% to the team mailbox. A forgotten resource then shows up in days, not at month end.

3. Turn on Organizations and create OUs​

  1. Open AWS Organizations, Create an organization. All features are on by default; keep them.
  2. Under AWS accounts, select Root, Actions, Create new, and create three OUs: Workloads, Shared, Sandbox.

4. Create the member accounts​

AWS accounts, Add an AWS account, Create an AWS account, once per account:

Account nameEmailOU
example-devaws-root+dev@example.comWorkloads
example-stagingaws-root+staging@example.comWorkloads
example-prodaws-root+prod@example.comWorkloads
example-sharedaws-root+shared@example.comShared
example-sandboxaws-root+sandbox@example.comSandbox

Or with the CLI, signed in to the management account:

aws organizations create-account \
--email aws-root+dev@example.com --account-name example-dev
aws organizations list-accounts --query 'Accounts[].[Name,Id,Status]' --output table
aws organizations move-account --account-id <dev-id> \
--source-parent-id <root-id> --destination-parent-id <workloads-ou-id>

Leave the role name as the default, OrganizationAccountAccessRole.

5. Set up single sign-on with IAM Identity Center​

  1. Open IAM Identity Center in the main region and choose Enable. Pick the region carefully; moving it later means setting everything up again.

  2. Settings, Identity source: keep the Identity Center directory, or connect the client's provider (Entra ID, Google Workspace) if they want staff to use company logins. If you connect one, check its plan lets you assign apps to groups, or you will assign every person by hand.

  3. Settings, Authentication: require MFA for every sign-in.

  4. Groups: create admins, developers, prod-operators, read-only.

  5. Permission sets, Create permission set, Predefined:

    Permission setBased on
    AdminAdministratorAccess
    DeveloperPowerUserAccess
    ProdOperatorViewOnlyAccess, plus CloudWatch Logs read
    ReadOnlyReadOnlyAccess
  6. AWS accounts: select an account, Assign users or groups, choose the group and permission set:

    Groupdevstagingprod
    adminsAdminAdminAdmin
    developersDeveloperDevelopernone
    prod-operatorsReadOnlyReadOnlyProdOperator
    read-onlyReadOnlyReadOnlyReadOnly
  7. Users: add each person (or they arrive from the client's provider) and put them in groups.

Each person then sets up their laptop once:

aws configure sso # paste the AWS access portal URL from the Identity Center dashboard
aws sso login --profile example-dev
aws sts get-caller-identity --profile example-dev

6. Turn on the guardrails​

GuardrailHowWhy
Organisation CloudTrailCloudTrail, Create trail, tick Enable for all accounts in my organizationOne audit log of every action in every account
GuardDutyGuardDuty in the management account, delegate admin to example-shared, enable for all members, in the main region and us-east-1Detects stolen keys and unusual activity
S3 Block Public AccessIn each account: S3, Block Public Access settings for this account, all onNo bucket goes public by accident
Service control policiesOrganizations, Policies, Service control policies, enable, create, attach to the Workloads OURules even an admin inside the account cannot break

Two SCPs to start with. Deny leaving the organisation:

{
"Version": "2012-10-17",
"Statement": [
{ "Effect": "Deny", "Action": "organizations:LeaveOrganization", "Resource": "*" }
]
}

Deny regions you do not use (global services are excepted):

{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Deny",
"NotAction": ["iam:*", "organizations:*", "sts:*", "route53:*", "cloudfront:*",
"support:*", "budgets:*", "ce:*", "health:*", "waf:*", "wafv2:*"],
"Resource": "*",
"Condition": { "StringNotEquals": { "aws:RequestedRegion": ["ap-south-1", "us-east-1"] } }
}]
}

Attach SCPs to the Workloads OU first and test in dev. A too-broad deny breaks things in ways that are hard to trace.

7. Create a Terraform state bucket in each account​

Terraform keeps a state file describing what it created. It lives in an S3 bucket in the same account.

P=example-dev
aws s3api create-bucket --bucket $P-tfstate --region ap-south-1 \
--create-bucket-configuration LocationConstraint=ap-south-1 --profile $P
aws s3api put-bucket-versioning --bucket $P-tfstate \
--versioning-configuration Status=Enabled --profile $P
aws s3api put-public-access-block --bucket $P-tfstate --profile $P \
--public-access-block-configuration BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true
terraform {
backend "s3" {
bucket = "example-dev-tfstate"
key = "envs/dev/terraform.tfstate"
region = "ap-south-1"
encrypt = true
use_lockfile = true # S3-native locking, Terraform 1.10 and later
}
}

8. Request quota increases​

New accounts start with low limits. In Service Quotas in each workload account, request what the project needs before launch week, for example Fargate vCPUs and SES sending (see Email).

Check it worked​

aws organizations list-accounts --output table # five accounts, ACTIVE
aws sso login --profile example-prod
aws sts get-caller-identity --profile example-prod # your SSO role, not a user
aws cloudtrail describe-trails --profile example-prod # the organisation trail

Ask a developer to sign in: they must reach dev and staging, and be refused in prod.

If it goes wrong​

You seeIt means
"Email address is already associated with an AWS account"Each account needs a unique email. Use the +alias
A person sees no accounts in the access portalThey are in no group, or the group has no assignment
"Access denied" in staging after attaching an SCPThe region SCP is missing a global service or region. Test SCPs in dev first
terraform init says the bucket does not existWrong profile or region in the backend block

Rules​

Rule

Nothing runs in the management account except Organizations, billing and Identity Center. If it is compromised, every account is.

Rule

No IAM users for people, and no long-lived access keys on laptops. Everyone uses SSO. Once people have IAM users, moving them to SSO becomes a project of its own.

Rule

Code chooses its environment from an explicit setting, never from "which account am I in".

Mistakes we made​

  • Production had seven IAM users and none had MFA, because people were added before SSO existed.
  • The client's identity provider plan could not assign apps to groups, so people had to be assigned one by one.
  • No CloudTrail or GuardDuty for months in the first production account.

Done when​

  • Management account with root MFA, billing access for IAM, budget alarm.
  • Organizations with Workloads, Shared, Sandbox OUs and five member accounts.
  • Identity Center with MFA, groups, permission sets, assignments.
  • Every team member has signed in with aws sso login.
  • Organisation CloudTrail, GuardDuty, S3 Block Public Access, SCPs.
  • Terraform state bucket in each account.
  • Quota requests sent.