Home / AWS

AWSHow-toBeginner

Introduction to AWS Organizations: management account, OUs and SCPs for a small team

What AWS Organizations gives you, how the management account, member accounts and OUs fit together, and how to create accounts, move them and attach a first SCP from the CLI.

Deepak Bhardwaj5 min read

Applies toAWS Organizations (all features), AWS CLI v2

Why bother with Organizations

Running everything in one AWS account works until it doesn't: a test script deletes a production bucket, a developer's IAM user has admin everywhere, and the bill can't be split by team. AWS Organizations lets you:

  • Run many accounts (prod, dev, security, logging) under one consolidated bill.
  • Group accounts into organizational units (OUs) and apply policies to the whole group.
  • Set service control policies (SCPs): hard limits that even an account's admin can't bypass.
  • Turn on org-wide services like CloudTrail, Config, GuardDuty and IAM Identity Center once.

The building blocks

Term What it is
Management account The account that creates the organization and pays the bill. Keep workloads out of it.
Member account Any other account in the org. Created from the org or invited.
Root The top of the OU tree (not the same as the root user).
OU A folder of accounts. Policies attached to an OU apply to every account inside it.
SCP A JSON policy that sets the maximum permissions for accounts under it. It never grants anything.

A common starter layout:

Root
├── Security      (log-archive, security-tooling)
├── Infrastructure (shared-network)
├── Workloads
│   ├── Prod      (app1-prod, app2-prod)
│   └── NonProd   (app1-dev, app1-test)
└── Sandbox       (individual experiments)

Step 1: Create the organization

From the account that will be the management account:

aws organizations create-organization --feature-set ALL
aws organizations describe-organization

ALL features is required for SCPs. If you already have an org with consolidated billing only, enable all features from the console (members must approve).

Step 2: Create OUs

ROOT_ID=$(aws organizations list-roots --query 'Roots[0].Id' --output text)

aws organizations create-organizational-unit --parent-id $ROOT_ID --name Security
aws organizations create-organizational-unit --parent-id $ROOT_ID --name Workloads
aws organizations create-organizational-unit --parent-id $ROOT_ID --name Sandbox

aws organizations list-organizational-units-for-parent --parent-id $ROOT_ID \
  --query 'OrganizationalUnits[].[Name,Id]' --output table

Step 3: Create a member account

aws organizations create-account \
  --email aws-app1-dev@example.com \
  --account-name app1-dev
# Returns a CreateAccountStatus Id; poll it
aws organizations describe-create-account-status --create-account-request-id <car-id>

Each account needs a unique email address. Use a shared mailbox or plus-addressing your mail system supports, not a person's inbox.

The new account gets a role called OrganizationAccountAccessRole that the management account can assume:

aws sts assume-role \
  --role-arn arn:aws:iam::<new-account-id>:role/OrganizationAccountAccessRole \
  --role-session-name setup

Move it into the right OU:

aws organizations move-account --account-id <new-account-id> \
  --source-parent-id $ROOT_ID --destination-parent-id <workloads-ou-id>

Step 4: Attach a first SCP

Enable the policy type, then create and attach a policy. This one stops anyone in Sandbox accounts from leaving the org, disabling CloudTrail, or using regions you don't operate in:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyLeaveOrg",
      "Effect": "Deny",
      "Action": "organizations:LeaveOrganization",
      "Resource": "*"
    },
    {
      "Sid": "ProtectCloudTrail",
      "Effect": "Deny",
      "Action": ["cloudtrail:StopLogging", "cloudtrail:DeleteTrail"],
      "Resource": "*"
    },
    {
      "Sid": "DenyOtherRegions",
      "Effect": "Deny",
      "NotAction": ["iam:*", "organizations:*", "sts:*", "support:*", "cloudfront:*", "route53:*", "budgets:*"],
      "Resource": "*",
      "Condition": {
        "StringNotEquals": { "aws:RequestedRegion": ["ap-south-1", "us-east-1"] }
      }
    }
  ]
}
aws organizations enable-policy-type --root-id $ROOT_ID --policy-type SERVICE_CONTROL_POLICY

POLICY_ID=$(aws organizations create-policy --name sandbox-guardrails \
  --type SERVICE_CONTROL_POLICY --description "Sandbox baseline" \
  --content file://sandbox-scp.json --query 'Policy.PolicySummary.Id' --output text)

aws organizations attach-policy --policy-id $POLICY_ID --target-id <sandbox-ou-id>

Warning

SCPs don't affect the management account, but they do affect every principal in member accounts, including the account root user. Test a new SCP on the Sandbox OU first. A badly written region-deny can break global services (IAM, Route 53, CloudFront are served from us-east-1), which is why they sit in NotAction above.

How SCPs combine with IAM

An action is allowed only if every layer allows it:

SCPs on Root → OU → account   (maximum)
        ∩
IAM identity policy           (what the user/role is granted)
        ∩
Permission boundary / resource policy (if used)

FullAWSAccess is attached everywhere by default, which is why nothing changes until you add Deny SCPs. Keep it attached and use deny-list SCPs; it's simpler than allow-lists.

Useful checks

# Which policies apply to an account?
aws organizations list-policies-for-target --target-id <account-id> --filter SERVICE_CONTROL_POLICY

# Where is an account in the tree?
aws organizations list-parents --child-id <account-id>

# All accounts
aws organizations list-accounts --query 'Accounts[].[Name,Id,Status]' --output table

When a user gets AccessDenied with "with an explicit deny in a service control policy" in the message, it's an SCP, not IAM.

Prevent problems later

  • No workloads and no daily logins in the management account. Lock its root user with MFA.
  • Use IAM Identity Center for human access instead of IAM users in each account.
  • Turn on an organization trail in CloudTrail and send it to a log-archive account.
  • Keep SCPs in Git and deploy them through a pipeline; document every Deny with a Sid.
  • Set an AWS Budgets alert in each account the day it's created.