AWS for DevOps — AWS Organizations

4 minute read

2.5 — AWS Organizations

AWS Organizations lets you manage multiple AWS accounts from a single management account. In real-world DevOps, you almost never run everything in one account — you separate environments, teams, and workloads across accounts for security and billing isolation.

Why Multiple Accounts?

Single Account Multiple Accounts
One blast radius — a mistake affects everything Isolated blast radius per account
Shared billing — hard to track costs per team Per-account billing and cost allocation
IAM becomes complex at scale Clean separation of concerns
One set of service limits Independent service limits per account

Organization Structure

  Management Account (root)
  │
  ├── OU: Production
  │   ├── Account: prod-app (111111111111)
  │   └── Account: prod-data (222222222222)
  │
  ├── OU: Staging
  │   └── Account: staging (333333333333)
  │
  ├── OU: Development
  │   ├── Account: dev-team-a (444444444444)
  │   └── Account: dev-team-b (555555555555)
  │
  └── OU: Security
      ├── Account: audit (666666666666)
      └── Account: logging (777777777777)

OU = Organizational Unit — a container for accounts that lets you apply policies at the group level.

Creating an Organization

# Create the organization (run from the management account)
$ aws organizations create-organization --feature-set ALL
{
    "Organization": {
        "Id": "o-abc123def4",
        "MasterAccountId": "123456789012",
        "FeatureSet": "ALL"
    }
}

# Create an OU under the root
$ aws organizations list-roots --query 'Roots[0].Id' --output text
r-abc1

$ aws organizations create-organizational-unit \
    --parent-id r-abc1 \
    --name Production
{
    "OrganizationalUnit": {
        "Id": "ou-abc1-prod1234",
        "Name": "Production"
    }
}

Creating a Member Account

$ aws organizations create-account \
    --email prod-app@example.com \
    --account-name prod-app
{
    "CreateAccountStatus": {
        "Id": "car-abc123",
        "AccountName": "prod-app",
        "State": "IN_PROGRESS"
    }
}

# Check status
$ aws organizations describe-create-account-status --create-account-request-id car-abc123
{
    "CreateAccountStatus": {
        "AccountId": "111111111111",
        "State": "SUCCEEDED"
    }
}

# Move account to Production OU
$ aws organizations move-account \
    --account-id 111111111111 \
    --source-parent-id r-abc1 \
    --destination-parent-id ou-abc1-prod1234

Service Control Policies (SCPs)

SCPs are guardrails that restrict what accounts in an OU can do. They do not grant permissions — they set the maximum permissions allowed.

  SCP says: "No EC2 outside ap-south-1"
  IAM says: "Allow ec2:*"
  
  Result: EC2 allowed ONLY in ap-south-1
  
  SCP ∩ IAM Policy = Effective Permissions

Example — Deny all actions outside allowed regions:

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

Note: Some global services (IAM, Organizations, STS) must be excluded because they only run in us-east-1.

Example — Prevent member accounts from leaving the organization:

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

Attaching an SCP

# Attach SCP to an OU
$ aws organizations attach-policy \
    --policy-id p-abc123 \
    --target-id ou-abc1-prod1234

# List policies attached to an OU
$ aws organizations list-policies-for-target \
    --target-id ou-abc1-prod1234 \
    --filter SERVICE_CONTROL_POLICY

Common DevOps Account Strategy

Account Purpose SCP
Management Billing, Organizations Minimal — only org management
Security / Audit CloudTrail, Config, GuardDuty Read-only to other accounts
Shared Services ECR, shared VPC, DNS Allow networking + container services
Production Live workloads Deny region changes, deny resource deletion
Staging Pre-production testing Same policies as prod
Development Developer sandboxes Relaxed — allow experimentation

Consolidated Billing

All accounts in the organization share a single bill:

  • Volume discounts — combined usage across accounts qualifies for lower pricing tiers
  • Reserved Instance sharing — an RI purchased in one account can apply to usage in another
  • Cost allocation tags — tag resources to track costs per team, project, or environment
# View the organization's accounts
$ aws organizations list-accounts \
    --query 'Accounts[*].[Id,Name,Status]' \
    --output table
-------------------------------------------
|            ListAccounts                  |
+--------------+----------+--------+
| 123456789012 | mgmt     | ACTIVE |
| 111111111111 | prod-app | ACTIVE |
| 333333333333 | staging  | ACTIVE |
+--------------+----------+--------+

Cross-Account Access Pattern

The management account creates a role in each member account that trusted accounts can assume:

# From the management account, assume a role in the prod account
$ aws sts assume-role \
    --role-arn arn:aws:iam::111111111111:role/OrganizationAccountAccessRole \
    --role-session-name admin-session

For CI/CD, your pipeline account assumes deployment roles in each target account.

Common Mistakes

Mistake Why It Hurts Fix
Running everything in one account No isolation — one mistake affects all Use Organizations
Overly restrictive SCPs on management account Can lock yourself out Never attach restrictive SCPs to management account
No SCP testing Policy breaks production Test in dev OU first
Not using consolidated billing tags Cannot track costs per team Enable cost allocation tags

Quick Reference

Task Command
Create organization aws organizations create-organization --feature-set ALL
Create OU aws organizations create-organizational-unit --parent-id ID --name NAME
Create account aws organizations create-account --email EMAIL --account-name NAME
Move account aws organizations move-account --account-id ID --source-parent-id SRC --destination-parent-id DST
Create SCP aws organizations create-policy --type SERVICE_CONTROL_POLICY --name NAME --content file://scp.json
Attach SCP aws organizations attach-policy --policy-id ID --target-id TARGET
List accounts aws organizations list-accounts

Next → IAM & Security Exercises

Updated: