Cloud Infrastructure · Identity and Access
Permission Boundaries and Service Control Policies as Independent Ceilings
How two documents that grant nothing restrict what other documents grant, what each one cannot cap, and how their scopes differ by blast radius.
Two documents in this model can restrict without granting: the permissions boundary, which caps one identity, and the service control policy, which caps every identity in a set of accounts. They look similar in syntax and are frequently confused in review, but they answer different questions, fail differently, and cap different channels. The differences are worth knowing because each one has a bypass that the other does not.
A boundary caps the identity channel only
A permissions boundary is attached to a user or a role and is expressed as an ordinary policy document with Allow statements. Its statements do not grant anything. They define the maximum set that the identity’s own policies can grant, and the effective permission of the identity is the intersection of the two.
The consequence is that a boundary can only ever narrow. Attaching a boundary to an identity that already has fewer permissions changes nothing. Attaching a broad boundary changes nothing. The only observable effect is when the identity’s policies allow something the boundary does not, and that permission disappears.
Boundaries are the instrument for delegating identity creation. An administrator who must let a team create roles cannot grant iam:CreateRole and iam:AttachRolePolicy without also granting the ability to create a role with any policy, which is equivalent to granting everything. With a boundary, the team’s own policies are capped at a known set, and any role they create must carry the same boundary. The creation permission is still dangerous, but the danger is bounded by the ceiling rather than unbounded.
The enforcement of that chaining is the part that is easy to miss. A principal with the ability to create roles but no condition requiring the boundary can create a role without one, and that role escapes the ceiling. The condition that closes this is a request-time test on the boundary parameter: the create and attach calls are allowed only when the request names the boundary, and only when the boundary is the expected one. Without that condition, the boundary is a suggestion for cooperative callers.
The second gap is structural. A boundary caps grants that arrive through identity policies. It does not cap grants that arrive through a resource-based policy in the same account. A bucket policy that names the bounded role as principal will grant access to that role regardless of the boundary, because the resource policy is a separate channel and the boundary only filters the identity channel. That is not a defect in the implementation so much as a scope decision, but it means a boundary is not the answer to “can this identity reach this data” unless the resource policies in the account are also controlled.
A service control policy caps the account
A service control policy attaches to an organization root, an organizational unit, or an account, and applies to every principal in the accounts beneath it. Like a boundary, it grants nothing. Unlike a boundary, it caps every channel: identity policies, resource policies, and even the root user of the member account.
The scope is the point. A boundary is a per-identity instrument used by an account’s own administrators. A service control policy is a cross-account instrument used by the organization that owns the accounts, and it is the only mechanism in the model that a member account cannot override. A member account administrator with full identity permissions still cannot perform an action the service control policy omits, and cannot grant it to anyone else.
The typical use is to remove whole classes of action from an environment rather than to grant anything. Denying the ability to leave the organization, to disable a logging service, to create resources outside a permitted region set, or to use a service the organization has not approved. Each of these is expressed as a Deny, because a service control policy that only ever contains Allow statements is being used as a boundary at account scope, which works but is a weaker form: an allow-list at account scope means every new service the organization adopts requires a policy edit before it can be used at all.
The two styles have different failure modes. An allow-list service control policy fails closed: a service nobody anticipated is unusable, and the failure appears as a permission error in an account whose own policies look correct. A deny-list policy fails open: a service nobody anticipated is usable, and the failure is a gap in coverage that nobody sees until it matters. Organizations that operate many accounts tend to mix the two, with a deny-list at the root for a small number of actions that must never occur, and allow-lists on specific units where the set of permitted services is genuinely small.
The intersection, and where it is computed
The evaluation order places explicit deny first, then the gates, then the allows. A service control policy contributes to the gate set at account scope, a boundary at identity scope, and a session policy at credential scope. All three are intersections with the identity grant, and all three are checked before any allow is considered final.
The practical difference between the two instruments is not the algebra but the locus of the edit. Changing a boundary requires access to the identity, in the account that owns it. Changing a service control policy requires access to the organization, which is a different credential with a different blast radius. An account owner cannot widen their own ceiling; an organization owner can widen every ceiling at once.
That asymmetry is the design constraint. It exists because the two failure modes are different. A boundary that is too tight breaks one identity and can be fixed by the identity’s owner. A service control policy that is too tight breaks an entire unit and must be fixed by the organization, which is a slower and more deliberate operation. The instrument is matched to the size of the mistake it can make.
Testing a ceiling before it is applied
Because both documents only ever remove permissions, the way to validate one is to compare the set it permits against the set the workloads actually use. That comparison is not something a policy author can do by reading; it requires knowing the call history. The method for deriving a candidate permission set from recorded calls, and the reason a naive derivation undercounts, is in deriving least privilege from access history.
A ceiling has an additional validation problem that a grant does not: it must cover not only the calls the workload makes today but the calls it will make during its next deployment. A boundary that omits a permission needed by a deployment pipeline fails at deploy time, and the failure is attributed to the deployment rather than to the boundary. The practical discipline is to test a proposed ceiling against a recorded call set from a full deployment cycle, not from a steady-state window, and to accept that the set will need revision when the workload changes.
Interaction with the root user and with break-glass
A service control policy restricts the root user of a member account, which is the one place where the model’s usual rule that the account owner is sovereign does not hold. A boundary cannot restrict the root user, because the root user has no identity policy attachment to cap and no boundary to carry. The root user’s access is intrinsic.
This is why the root user is treated separately in every serious account design: it is the only principal whose permissions cannot be reduced by the mechanisms described here, so it must be protected by other means. Removing its access keys, enabling a hardware second factor, and storing the recovery material offline are the controls that remain available, and none of them is a policy.
The break-glass pattern is the operational consequence. An organization that applies a tight service control policy needs a documented path to relax it for a specific account during an incident, and that path is itself a privilege that must be protected. The path is usually a role in the management account with a trust policy restricted to a small set of humans, and it is audited by the same mechanism that records ordinary assumptions, described in role assumption and session tokens.
Why the syntax is the same and the semantics are not
Both documents use the same grammar as an identity policy, which invites the assumption that they behave the same way. They do not, in three respects that matter. A boundary applies to one identity and can be edited by that account; a service control policy applies to an account subtree and can only be edited above it. A boundary is bypassed by a same-account resource policy; a service control policy is not. And a boundary requires a condition on the create-role call to keep it from being escaped; a service control policy has no such escape because it is evaluated outside the account.
The last difference is the one that determines which instrument to reach for. If the goal is to let a team operate independently within a known envelope, the boundary is the right tool, because it is enforced at the point where the team makes changes and it fails in a place the team can see. If the goal is to guarantee that a class of action never happens in a set of accounts, the service control policy is the only tool that can make that guarantee, because it is evaluated by a party the account cannot influence. Using a boundary for the second goal leaves the account owner able to remove it; using a service control policy for the first goal makes every routine permission change an organization-level ticket.
The two are complements, not alternatives, and the ceiling they jointly impose is the intersection of both with the identity grant. A permission that a service control policy permits but a boundary omits is unavailable. A permission that both permit but no identity policy grants is also unavailable, which is the property that distinguishes a ceiling from a grant and the one that makes ceilings safe to apply broadly: they cannot create access, only remove it.
References
- Permissions boundaries for IAM entities — AWS Identity and Access Management User Guide
- Service control policies — AWS Organizations User Guide
- The Protection of Information in Computer Systems — Saltzer and Schroeder, Proceedings of the IEEE, 1975