A permission boundary is a managed policy that sets the ceiling on what an IAM user or role can ever do: effective permissions become the intersection of the identity policies and the boundary. Boundaries grant nothing on their own. Their real purpose is delegation — letting application teams create and manage their own roles, while a condition on iam:CreateRole forces every role they create to wear the boundary, so nobody can mint a role more powerful than the ceiling allows.
What’s happening
Without boundaries, IAM delegation is binary: either a central team creates every role (and becomes the bottleneck every deploy waits on), or teams get iam:* and any developer can create an admin role for themselves — privilege escalation as a feature. Boundaries break the deadlock. The mechanics: attach a managed policy as the boundary on a principal, and every request is evaluated against both the identity policy and the boundary; an action must be allowed by both to succeed (an explicit deny in either still wins over everything). The boundary is a ceiling, not a floor — a boundary allowing s3:* grants nothing to a role whose identity policy only allows dynamodb:GetItem.
The delegation pattern adds one enforcement trick. The team’s own role gets iam:CreateRole, iam:AttachRolePolicy, and friends — but only under a condition that iam:PermissionsBoundary equals the ARN of your standard boundary policy. Creating a role without the boundary, or with a different one, fails. Combined with explicit denies on removing or swapping boundaries and on editing the boundary policy itself, the ceiling becomes self-enforcing: teams move fast inside it, and cannot raise it. Two honest caveats. First, boundaries do not constrain resource-based policies — a bucket policy granting the specific user directly still works even if the boundary would not allow it. Second, boundaries are per-principal, not account-wide; for account-level ceilings you want SCPs, which our SCP baseline article covers. The evaluation-logic chapter of the IAM documentation shows exactly where boundaries sit in the decision chain.
What to check first
- Whether escalation is currently possible. Any principal holding
iam:CreateRoleplusiam:AttachRolePolicywithout a boundary condition can already self-escalate — that is the population to fix. - What the ceiling should contain. The boundary must allow everything teams legitimately deploy (their services, their regions) or it will be fought instead of adopted.
- Who can edit the boundary policy. A boundary editable by the people it constrains is decoration.
- Existing boundary usage — some accounts already have half-adopted boundaries from an old initiative, attached inconsistently.
How to verify
# Does this role have a boundary, and which one?
aws iam get-role --role-name team-app-role \
--query 'Role.PermissionsBoundary'
# Find principals allowed to create roles (candidates for the delegation pattern)
aws iam list-entities-for-policy \
--policy-arn arn:aws:iam::aws:policy/IAMFullAccess
# Test: as the delegated role, try creating a role WITHOUT the boundary — must fail
aws iam create-role --role-name escalation-test \
--assume-role-policy-document file://trust.json
Console path: IAM > Roles > (role) > Permissions boundary shows the attached ceiling. The create-role test should return an explicit deny mentioning the boundary condition; if it succeeds, the delegation policy is not doing its job — delete the test role.
The runbook
- Write the boundary policy as the ceiling. Allow the service families teams deploy (compute, storage, queues in approved regions) and omit what they must never touch:
organizations:*,account:*, CloudTrail write actions, and IAM except the reads. - Grant IAM powers only with the boundary condition attached. The core of the delegation policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "CreateRolesOnlyWithBoundary",
"Effect": "Allow",
"Action": ["iam:CreateRole", "iam:AttachRolePolicy", "iam:PutRolePolicy",
"iam:DetachRolePolicy", "iam:DeleteRolePolicy", "iam:UpdateRole"],
"Resource": "arn:aws:iam::111122223333:role/team-*",
"Condition": {
"StringEquals": {"iam:PermissionsBoundary": "arn:aws:iam::111122223333:policy/team-boundary"}
}
},
{
"Sid": "NoBoundaryTampering",
"Effect": "Deny",
"Action": ["iam:DeleteRolePermissionsBoundary", "iam:DeleteUserPermissionsBoundary"],
"Resource": "*"
},
{
"Sid": "NoEditingTheCeiling",
"Effect": "Deny",
"Action": ["iam:CreatePolicyVersion", "iam:DeletePolicy",
"iam:DeletePolicyVersion", "iam:SetDefaultPolicyVersion"],
"Resource": "arn:aws:iam::111122223333:policy/team-boundary"
}
]
}
- Constrain names with a path or prefix. The
team-*resource pattern keeps delegated roles in their own namespace and out of platform roles. - Pilot with one team before mandating. The first weeks surface every service the boundary forgot; expanding the ceiling deliberately beats teams routing around it.
- Alert on boundary-related denials and changes. CloudTrail events for
DeleteRolePermissionsBoundaryattempts and deniedCreateRolecalls show both attacks and friction — you want to see both.
When this is the wrong move
If the actual goal is “developers should not touch production,” account separation plus SCPs does that with far less machinery — boundaries are for delegating IAM authorship inside an account, not for general environment isolation. Skip them too when one central team happily owns all role creation at your scale; the pattern earns its complexity only once role-creation demand outgrows the platform team.
Common mistakes
- Expecting the boundary to grant access — it is only a ceiling, and an empty identity policy still means no permissions.
- Enforcing the boundary on
iam:CreateRolebut forgettingiam:PutRolePermissionsBoundaryand the delete actions, leaving a swap-the-ceiling loophole. - Letting the constrained team edit the boundary policy itself.
- Assuming boundaries stack with resource policies — a direct grant to the user in a bucket policy bypasses the ceiling.
Keeping it under control
- Keep the boundary policy in infrastructure-as-code with mandatory review from outside the teams it constrains.
- Diff
Role.PermissionsBoundaryacross all roles weekly; every delegated-namespace role must wear the standard boundary. - Version ceiling expansions like API changes: proposed, reviewed, announced.
- Pair boundaries with SCPs so the account has a ceiling even for principals someone forgot to bound.
Boundaries are the difference between “teams wait for the platform team” and “teams self-serve inside a ceiling nobody can raise from below” — genuinely advanced IAM, and worth it exactly when delegation pain is real. If nobody on your team truly owns the AWS environment end to end, that is the root of most of these problems — Stack Harbor runs AWS accounts daily under AWS Operations, and you can book an AWS review.