Docs / Organizations

AWS Organizations

LocalEmu implements the AWS Organizations API surface. You can create an organization, mint member accounts on demand, build an OU tree, attach Service Control Policies, and assume the management account into any member through the auto-seeded OrganizationAccountAccessRole the way you would on real AWS.

Create an organization

The account that calls CreateOrganization becomes the management account (formerly the master account). It cannot be changed afterwards, exactly like real AWS:

$ AWS_ACCESS_KEY_ID=420420420420 \
  AWS_SECRET_ACCESS_KEY=any awsemu organizations create-organization
{
  "Organization": {
    "Id": "o-au9g8di70o",
    "Arn": "arn:aws:organizations::420420420420:organization/o-au9g8di70o",
    "FeatureSet": "ALL",
    "MasterAccountId": "420420420420",
    "MasterAccountArn": "arn:aws:organizations::420420420420:account/o-au9g8di70o/420420420420",
    "MasterAccountEmail": "master@example.com",
    "AvailablePolicyTypes": [
      {"Type": "SERVICE_CONTROL_POLICY", "Status": "ENABLED"}
    ]
  }
}

Mint member accounts

CreateAccount returns synchronously with State: SUCCEEDED. The 12-digit ID is allocated by LocalEmu and is unique within the instance:

$ AWS_ACCESS_KEY_ID=420420420420 awsemu organizations create-account \
    --email member1@org.test --account-name member1
{
  "CreateAccountStatus": {
    "Id": "car-9kgilne5",
    "AccountName": "member1",
    "State": "SUCCEEDED",
    "AccountId": "528989751865"
  }
}

Every account in the org appears in ListAccounts, alongside the management account:

$ AWS_ACCESS_KEY_ID=420420420420 awsemu organizations list-accounts
{
  "Accounts": [
    {"Id": "420420420420", "Name": "master", "Email": "master@example.com",
     "Status": "ACTIVE", "JoinedMethod": "CREATED"},
    {"Id": "528989751865", "Name": "member1", "Email": "member1@org.test",
     "Status": "ACTIVE", "JoinedMethod": "CREATED"}
  ]
}

Organizational units

OUs nest up to five levels deep, just like real AWS. Find the organization root first, then create OUs under it and move accounts between them:

$ AWS_ACCESS_KEY_ID=420420420420 awsemu organizations list-roots \
    --query 'Roots[0].Id' --output text
r-v9uu

$ AWS_ACCESS_KEY_ID=420420420420 awsemu organizations create-organizational-unit \
    --parent-id r-v9uu --name dev
{
  "OrganizationalUnit": {
    "Id": "ou-v9uu-ahiswje4",
    "Arn": "arn:aws:organizations::420420420420:ou/o-au9g8di70o/ou-v9uu-ahiswje4",
    "Name": "dev"
  }
}

Service Control Policies

SCPs are stored, attached to roots / OUs / accounts, and surfaced via ListPoliciesForTarget, DescribePolicy, and DescribeEffectivePolicy. SCPs are stored, attached, and described, but not enforced: LocalEmu does not subtract SCP-denied actions from a principal's effective permissions yet. The tooling you use for SCP authoring round-trips correctly; the enforcement layer lands in a follow-up release.

$ AWS_ACCESS_KEY_ID=420420420420 awsemu organizations create-policy \
    --name DenyEC2 \
    --description "Block RunInstances" \
    --type SERVICE_CONTROL_POLICY \
    --content '{
  "Version":"2012-10-17",
  "Statement":[{
    "Effect":"Deny",
    "Action":"ec2:RunInstances",
    "Resource":"*"
  }]
}'

$ AWS_ACCESS_KEY_ID=420420420420 awsemu organizations attach-policy \
    --policy-id p-xxxxxxxx \
    --target-id r-v9uu

DescribeEffectivePolicy returns the merged AWS-shape document for the target:

$ AWS_ACCESS_KEY_ID=420420420420 awsemu organizations \
    describe-effective-policy --policy-type TAG_POLICY
{
  "EffectivePolicy": {
    "PolicyContent": "{\"tags\": {}}",
    "LastUpdatedTimestamp": 1780696951.871734,
    "PolicyType": "TAG_POLICY"
  }
}

OrganizationAccountAccessRole

Real AWS auto-creates an IAM role called OrganizationAccountAccessRole in every member account when it joins the org. LocalEmu does the same: the role's trust policy names the management account, and an inline AdministratorAccess policy is attached so the role actually has permissions the moment it is assumed.

The full flow the management account uses to operate inside a member:

# 1. management account assumes OrganizationAccountAccessRole in member
$ AWS_ACCESS_KEY_ID=420420420420 awsemu sts assume-role \
    --role-arn arn:aws:iam::528989751865:role/OrganizationAccountAccessRole \
    --role-session-name admin
{
  "AssumedRoleUser": {
    "Arn": "arn:aws:sts::528989751865:assumed-role/OrganizationAccountAccessRole/admin"
  },
  "Credentials": { ... }
}

# 2. the returned session acts as the new member account
$ AWS_ACCESS_KEY_ID=<session-key> \
  AWS_SECRET_ACCESS_KEY=<session-secret> \
  AWS_SESSION_TOKEN=<session-token> \
  awsemu sts get-caller-identity
{
  "Account": "528989751865",
  "Arn": "arn:aws:sts::528989751865:assumed-role/OrganizationAccountAccessRole/admin"
}

Confirming the inline policy lives in the member account:

$ AWS_ACCESS_KEY_ID=528989751865 awsemu iam get-role-policy \
    --role-name OrganizationAccountAccessRole \
    --policy-name AdministratorAccess
{
  "PolicyDocument": {
    "Version": "2012-10-17",
    "Statement": [{
      "Effect": "Allow",
      "Action": "*",
      "Resource": "*"
    }]
  }
}

Supported operations

The Organizations API surface covers the standard 39 ops:

What is config-only

See also