Organizations
AWS Organizations control plane — accounts, OUs, SCPs, tag policies, handshakes, delegated administrators. Real SCP enforcement across services.
fakecloud implements 63 of 63 AWS Organizations operations at 100% Smithy conformance. SCPs (Service Control Policies) are really enforced as a permission ceiling across every IAM-evaluated service.
Supported features
- Organization lifecycle —
CreateOrganization,DescribeOrganization,DeleteOrganization,EnableAllFeatures. The management account is the only caller allowed to mutate org state, matching AWS. - Accounts
CreateAccount/CreateGovCloudAccountrun a real async lifecycle: the call returns immediately with aCreateAccountStatuswhoseStatestartsIN_PROGRESS, then transitions toSUCCEEDEDafter the background task provisions the member account, attaches the default IAM admin role (OrganizationAccountAccessRole), and assigns the account to the requested OU (or to the root).DescribeCreateAccountStatusandListCreateAccountStatusreflect the real status.CloseAccountmoves the account toSUSPENDEDand blocks subsequent control-plane calls from that account ID — the management account is protected and cannot be closed.RemoveAccountFromOrganizationdetaches a member account from the org; subsequent describes flip toNotFound, mirroring AWS's behaviour where the account becomes a standalone payer.LeaveOrganizationlets the calling member account remove itself; the management account is refused withMasterCannotLeaveOrganizationException, and a caller that isn't a member getsAccountNotFoundException.DescribeAccount,ListAccounts,ListAccountsForParentpaginate the directory.InviteAccountToOrganization+AcceptHandshake/DeclineHandshake/CancelHandshake/DescribeHandshake+ListHandshakesForAccount/ListHandshakesForOrganizationrun the real handshake state machine — only the invited account can accept/decline, only the inviter can cancel, and expired handshakes flip toEXPIRED.
- Organizational Units —
CreateOrganizationalUnit,UpdateOrganizationalUnit,DeleteOrganizationalUnit,DescribeOrganizationalUnit,ListOrganizationalUnitsForParent,ListChildren,ListParents,ListRoots,MoveAccount. The hierarchy is enforced — non-empty OUs cannot be deleted, andMoveAccountvalidates both source and destination parents. - Policies —
CreatePolicy,UpdatePolicy,DeletePolicy,DescribePolicy,ListPolicies,ListPoliciesForTarget,ListTargetsForPolicy,AttachPolicy,DetachPolicy,EnablePolicyType,DisablePolicyType,DescribeEffectivePolicy. The fullPolicyTypeenum is accepted on the list filters; the four types fakecloud manages (SERVICE_CONTROL_POLICY,TAG_POLICY,BACKUP_POLICY,AISERVICES_OPT_OUT_POLICY) can be created — others returnPolicyTypeNotAvailableForOrganizationException, an out-of-enum value returnsInvalidInputException. Policy documents are JSON-validated on create/update — malformed content is rejected withMalformedPolicyDocumentException. - Effective-policy validation —
ListAccountsWithInvalidEffectivePolicyandListEffectivePolicyValidationErrorsreturn the honest empty result: fakecloud stores only well-formed policies, so no account ever has an invalid effective policy. - Billing responsibility transfers —
InviteOrganizationToTransferResponsibilityopens a handshake-backedBILLINGtransfer;DescribeResponsibilityTransfer,UpdateResponsibilityTransfer(rename),TerminateResponsibilityTransfer(->WITHDRAWN), andListInboundResponsibilityTransfers/ListOutboundResponsibilityTransfersoperate over the transfer records, filtered by direction. - Resource policies —
PutResourcePolicy,DescribeResourcePolicy,DeleteResourcePolicyfor the org-wide delegation policy. - Service access —
EnableAWSServiceAccess,DisableAWSServiceAccess,ListAWSServiceAccessForOrganization,RegisterDelegatedAdministrator,DeregisterDelegatedAdministrator,ListDelegatedAdministrators,ListDelegatedServicesForAccount. Delegated-admin registration is gated on the service havingEnableAWSServiceAccessfirst, matching AWS error ordering. - Tagging —
TagResource,UntagResource,ListTagsForResourceon accounts, OUs, roots, and policies.
SCP enforcement
Service Control Policies aren't just stored — they're a real permission ceiling. When FAKECLOUD_IAM=strict is on, every IAM evaluation walks up from the calling account's parent OU(s) through the root, collects the SCPs that apply at each level, and intersects them with the identity-based policy decision. The semantics match AWS:
- Same-target SCPs are unioned (multiple SCPs attached to the same OU/account can each grant a subset).
- Cross-level SCPs are intersected (a permission must be allowed at every level — root, parent OU, account — to survive).
- The management account and service-linked roles are exempt, just like AWS.
- An explicit
Denyat any level wins.
This means a parent OU with Deny: s3:DeleteBucket actually blocks the call in a member account even when the member's IAM policy grants s3:*. The same machinery enforces TAG_POLICY constraints on TagResource calls when strict mode is on.
Protocol
JSON 1.1. X-Amz-Target: AWSOrganizationsV20161128.<Action>.
Bootstrap
Because Organizations sits above IAM, fakecloud exposes POST /_fakecloud/iam/create-admin to seed a management-account admin without needing existing credentials. Once the management account is bootstrapped, the rest of the org lifecycle uses normal SigV4'd calls.
Smoke test
fakecloud &
# Bootstrap a management-account admin.
curl -fsS -X POST http://localhost:4566/_fakecloud/iam/create-admin \
-H 'content-type: application/json' \
-d '{"AccountId":"123456789012"}'
aws --endpoint-url http://localhost:4566 organizations create-organization \
--feature-set ALL
aws --endpoint-url http://localhost:4566 organizations create-account \
--email dev@example.com --account-name Dev
aws --endpoint-url http://localhost:4566 organizations list-create-account-status \
--states SUCCEEDED IN_PROGRESS
aws --endpoint-url http://localhost:4566 organizations create-policy \
--type SERVICE_CONTROL_POLICY \
--name DenyBucketDelete \
--description "block deletes" \
--content '{"Version":"2012-10-17","Statement":[{"Effect":"Deny","Action":"s3:DeleteBucket","Resource":"*"}]}'Introspection
GET /_fakecloud/organizations/accounts returns every member account in the org with lifecycle state, parent OU, tags, and directly-attached SCPs — useful for asserting org shape from tests without management-account credentials (the endpoint bypasses IAM).
curl -fsS http://localhost:4566/_fakecloud/organizations/accounts | jqResponse shape:
{
"accounts": [
{
"id": "111111111111",
"arn": "arn:aws:organizations::111111111111:account/o-abc/111111111111",
"email": "111111111111@example.com",
"name": "Account 111111111111",
"status": "ACTIVE",
"joinedMethod": "INVITED",
"joinedTimestamp": "2026-05-11T00:00:00Z",
"parentOuId": "r-1234",
"tags": [],
"scpAttached": []
}
],
"managementAccountId": "111111111111",
"masterAccountId": "111111111111"
}scpAttached lists SCPs attached directly to the account only — to resolve the full inherited set walk up the OU tree or call DescribeEffectivePolicy. accounts is empty (and the account-id fields null) when no organization has been created yet. masterAccountId mirrors managementAccountId for back-compat with the AWS field renamed in 2020.
The first-party SDKs wrap this:
- Rust:
fakecloud_sdk::FakeCloud::new(url).organizations().get_accounts() - Go:
fakecloud.New(url).Organizations().GetAccounts(ctx) - Python:
await fc.organizations.get_accounts()(async) orfc.organizations.get_accounts()(sync) - TypeScript:
await fc.organizations.getAccounts() - Java:
fc.organizations().getAccounts() - PHP:
$fc->organizations()->getAccounts()
GET /_fakecloud/organizations/responsibility-transfers returns every billing responsibility transfer in the org with direction (INBOUND/OUTBOUND), lifecycle status, source/target management accounts, and the active handshake id.
curl -fsS http://localhost:4566/_fakecloud/organizations/responsibility-transfers | jqResponse shape:
{
"responsibilityTransfers": [
{
"id": "rt-0123456789abcdef0123456789abcdef",
"arn": "arn:aws:organizations::111111111111:responsibilitytransfer/o-abc/rt-0123456789abcdef0123456789abcdef",
"name": "my-billing-transfer",
"type": "BILLING",
"status": "REQUESTED",
"direction": "OUTBOUND",
"sourceManagementAccountId": "111111111111",
"sourceManagementAccountEmail": "admin@example.com",
"targetManagementAccountId": "222222222222",
"targetManagementAccountEmail": "222222222222@example.com",
"startTimestamp": "2026-05-29T00:00:00+00:00",
"endTimestamp": null,
"activeHandshakeId": "h-0123456789abcdef0123456789abcdef"
}
]
}endTimestamp and activeHandshakeId are null when not set; the list is empty when no organization exists. Transfers are sorted by id.
The first-party SDKs wrap this:
- Rust:
fakecloud_sdk::FakeCloud::new(url).organizations().get_responsibility_transfers() - Go:
fakecloud.New(url).Organizations().GetResponsibilityTransfers(ctx) - Python:
await fc.organizations.get_responsibility_transfers()(async) orfc.organizations.get_responsibility_transfers()(sync) - TypeScript:
await fc.organizations.getResponsibilityTransfers() - Java:
fc.organizations().getResponsibilityTransfers() - PHP:
$fc->organizations()->getResponsibilityTransfers()
Gotchas
- Management account only. Mutating calls (
CreateAccount,AttachPolicy,EnableAWSServiceAccess, etc.) must originate from the management account. Calls from member accounts returnAccessDeniedException, matching AWS. - SCP enforcement is opt-in. SCPs are stored and
DescribeEffectivePolicyworks in all modes, but enforcement only kicks in underFAKECLOUD_IAM=strict(orsoftfor log-only). See SigV4 verification and IAM enforcement. - CreateAccount is async. The state moves through
IN_PROGRESS->SUCCEEDEDover a short interval; tests should pollDescribeCreateAccountStatusrather than assume the account is usable immediately.