AWS Organizations
Organizations control plane + SCP enforcement as the top-of-chain IAM evaluator layer.
fakecloud ships a minimal AWS Organizations implementation. Its purpose is to let you attach Service Control Policies (SCPs) to accounts and organizational units so your tests can exercise the full IAM evaluation hierarchy — SCP ceiling, permission boundary, session policy, identity policy, resource policy — end to end.
Both the control plane and SCP enforcement are live. SCPs act as the top-of-chain allow-list ceiling: see the security reference for the full evaluation order and the management-account and service-linked-role exemptions.
Model
- Many independent organizations per fakecloud process.
CreateOrganizationsets the caller's account as the management account and seeds a root OU, and rejects only when that account is already in an organization (AlreadyInOrganizationException) -- another account having created one does not block it. - An account belongs to at most one organization. Inviting an account another organization already holds is
HandshakeConstraintViolationExceptionwithReason: ALREADY_IN_AN_ORGANIZATION, both at invite time and again when the handshake is accepted. - Organizations never see each other: every read resolves the caller's own organization, and a caller in none gets
AWSOrganizationsNotInUseException-- the same answer as a process with no organizations at all. The exceptions are the operations that are cross-organization by nature, and they stay scoped to the caller's own involvement:ListHandshakesForAccountandDescribeHandshake(the account answering an invitation is not yet a member of the inviting organization, so a handshake resolves by id, readable by its two parties and by members of the organization that owns it), and the responsibility-transfer ops (a transfer is recorded once, in the source organization, and both management accounts are parties to it). FullAWSAccessis auto-created and auto-attached to the root OU onCreateOrganization, matching AWS. Its content:{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"*","Resource":"*"}]}- Both feature sets are supported.
CONSOLIDATED_BILLINGcreates the org with no policy types enabled, so SCPs are unavailable — matching AWS. Any otherFeatureSetvalue isInvalidInputException. - Only the management account can run write ops (
CreateOrganizationalUnit,MoveAccount,DeleteOrganization, etc.). A member but non-management caller getsAccessDeniedException. A non-member caller getsAWSOrganizationsNotInUseExceptionso org existence itself does not leak. - Bootstrapping an admin via
/_fakecloud/iam/create-adminleaves the account standalone — it joins no organization, matching AWS, where a freshly vended account belongs to no organization until it is invited and accepts, or is created throughCreateAccount. Pass"organizationId": "o-..."in the request body to enroll the account into that organization's root OU instead, the shortcut equivalent of anInviteAccountToOrganization+AcceptHandshakepair.
Supported operations
| Operation | Status | Notes |
|---|---|---|
CreateOrganization | ✅ | FeatureSet ALL or CONSOLIDATED_BILLING; caller becomes management |
DescribeOrganization | ✅ | Returns AWSOrganizationsNotInUseException to non-members |
DeleteOrganization | ✅ | Management only; fails if any non-management members remain |
ListRoots | ✅ | Returns the single root |
CreateOrganizationalUnit | ✅ | Management only; name must be unique under the parent |
UpdateOrganizationalUnit | ✅ | Rename; duplicate-name check |
DeleteOrganizationalUnit | ✅ | Fails with OrganizationalUnitNotEmptyException if children remain |
DescribeOrganizationalUnit | ✅ | |
ListOrganizationalUnitsForParent | ✅ | |
ListAccounts | ✅ | Returns all members |
ListAccountsForParent | ✅ | |
DescribeAccount | ✅ | |
MoveAccount | ✅ | Enforces exact source-parent match |
CreatePolicy | ✅ | Type=SERVICE_CONTROL_POLICY only; structural JSON validation |
UpdatePolicy | ✅ | Blocks mutation of AWS-managed policies (FullAWSAccess) |
DeletePolicy | ✅ | Blocks deletion when attached or AWS-managed |
DescribePolicy | ✅ | |
ListPolicies | ✅ | Filter required and must be SERVICE_CONTROL_POLICY |
AttachPolicy | ✅ | Idempotent re-attach; targets = root / OU / account |
DetachPolicy | ✅ | Returns PolicyNotAttachedException on missing attachment |
ListPoliciesForTarget | ✅ | |
ListTargetsForPolicy | ✅ |
SCP semantics
Policies are JSON documents of the same shape as identity policies. The control plane validates structural parseability; the evaluator applies the same document at request time, so there is no divergence between what the control plane accepts and what actually gates traffic.
FullAWSAccess (p-FullAWSAccess) is created and attached to the root OU on CreateOrganization and is immutable: UpdatePolicy and DeletePolicy return PolicyChangesNotAllowedException for it. You can still DetachPolicy it from the root — AWS permits this, and tests that want to exercise a restrictive SCP posture rely on being able to drop the AWS-managed allow-all.
SCPs are enforced through the IAM evaluator when FAKECLOUD_IAM is soft or strict. Off by default when no organization exists or the resolver returns a non-member / management / service-linked-role principal. See the security reference for the full evaluation chain.
Usage
use aws_sdk_organizations::Client;
let client = Client::new(&sdk_config);
let created = client.create_organization().send().await?;
let org = created.organization().unwrap();
println!("{} managed by {}", org.id().unwrap(), org.master_account_id().unwrap());Behavior under FAKECLOUD_IAM=off
Organizations control plane works identically regardless of the FAKECLOUD_IAM setting. SCP enforcement is gated on IAM being enabled, so creating an organization with FAKECLOUD_IAM=off is allowed but has no effect on evaluation — the resolver is still wired, but the evaluator it feeds is never called.