Letting developers create their own IAM roles removes a bottleneck, but without a correctly built permissions boundary it also lets any developer create a role more powerful than themselves and use it. A permissions boundary is a managed policy that caps what an IAM user or role can ever be allowed to do, and the condition key iam:PermissionsBoundary lets you require that cap on every role a delegated administrator creates. Getting the pattern right takes more than one condition: the boundary must be impossible to remove, edit or swap, PassRole must be fenced, and resource-based policies have to be understood because some of them are not limited by a boundary at all. This guide walks through the delegation pattern end to end, from policy design to testing, detection and rollout. It is for cloud security engineers, platform teams and IAM owners. Policy basics are in the AWS IAM Policies and Roles Guide and organisation guardrails in the AWS Organizations and SCPs Guide.
This is an independent guide from NHI Mgmt Group, based on AWS's public documentation as of November 2026. It is not sponsored or reviewed by AWS. Product features change, so confirm settings against AWS's current documentation.
Key takeaways
- A boundary grants nothing. Effective permissions are the intersection of the identity-based policy and the boundary, and an explicit deny in either wins; access still needs an allow somewhere.
- Delegation needs five guards, not one. Require the boundary on create, block its removal, block edits to the boundary policy, fence role names and paths, and restrict iam:PassRole to delegated roles only.
- Put boundaries on the roles developers create, not on developers. AWS's own guidance applies boundaries to workload roles and uses SCPs to restrict whole accounts.
- Some resource-based policies bypass the boundary. Grants to an IAM user ARN or a role session ARN are not limited by a boundary's implicit deny; grants to a role ARN are.
- Test the escalation paths before go-live. Try to create an unbounded role, remove or replace the boundary, edit the boundary policy and pass an admin role, and confirm each fails and is logged.
What a permissions boundary is, and what it is not
AWS defines a permissions boundary as an advanced feature that uses a managed policy (AWS managed or customer managed) to set the maximum permissions an identity-based policy can grant to an IAM user or role. It applies to users and roles only, and each entity has at most one. The AWS Security Blog notes that a boundary is limited to 6,144 characters, the managed policy size limit, which is one reason to keep boundaries general rather than tailored per role.
How the boundary combines with other policy types matters for the delegation pattern:
- Identity-based policies: effective permissions are the intersection of the identity policy and the boundary.
- Session policies: a role session gets the intersection of identity policy, boundary and session policy.
- SCPs and RCPs: a request must also be allowed by the SCPs and RCPs that apply; any explicit deny anywhere ends evaluation with Deny.
- Resource-based policies: AWS states that grants to an IAM user ARN in the same account, grants made directly to a role session ARN, and grants using a wildcard principal with an aws:PrincipalArn condition are not limited by the boundary's implicit deny, while grants to an IAM role ARN are. Only an explicit deny in the boundary stops the first group.
AWS also warns against resource-based policies that combine Deny with NotPrincipal for principals that have a boundary, because the statement denies them regardless of the NotPrincipal values; use ArnNotEquals on aws:PrincipalArn instead.
Boundaries compared with other guardrails
| Mechanism | Applies to | Set by | Best used for | Limits |
|---|---|---|---|---|
| Permissions boundary | One IAM user or role | Whoever creates or edits the entity, controlled by iam:PermissionsBoundary conditions | Capping roles that developers or pipelines create inside an account | One per entity; 6,144 characters; resource-based grants to user or session ARNs not capped |
| Service control policy (SCP) | Every principal in member accounts of an OU or account, except the management account | Organisation administrators | Account-wide prohibitions: Regions, disabling logging, leaving the organisation | Does not grant; does not apply to the management account or service-linked roles |
| Resource control policy (RCP) | Resources in member accounts | Organisation administrators | Data perimeter: who outside the organisation can reach resources | Supported services only. See the AWS Resource Control Policies and Data Perimeter Guide |
| Session policy | One role session | The caller of AssumeRole or GetFederationToken | Narrowing a broad role for one task | Chosen by the caller, so not a control over the caller |
| Deny in identity policy | One user, role or group | IAM administrators | Specific exceptions where a boundary is not appropriate | Easy to forget on new principals |
The AWS Security Blog's guidance is explicit: an SCP is the better tool to restrict developers themselves, because one SCP restricts every principal in an account, while boundaries belong on the workload roles developers create. Use both together: SCPs fence the account, boundaries fence what delegated builders can create inside it.
The delegation pattern end to end
The pattern has three policies: the boundary that every created role must carry, the delegated administrator policy for developers or pipelines, and the organisation SCP that protects the platform's own roles. The examples below describe the structure; names such as DeveloperWorkloadBoundary and the applicationroles path are illustrative.
1. The boundary for workload roles
- Allow the service actions workloads need: for example S3, DynamoDB, SQS, CloudWatch Logs and KMS use. The aws-samples example-permissions-boundary repository recommends Allow statements only, no resources and no conditions in boundaries, and one reusable boundary rather than one per role. AWS's blog suggests a small set of boundaries by business function, such as system automation or analytics.
- Exclude IAM and identity services: leave iam:*, sts:AssumeRole into arbitrary roles, organizations:* and account-level security service changes out of the boundary, so a bounded role cannot create further identities or switch off detection. If bounded roles must create roles (nested delegation), the boundary must carry the same conditions as the delegated administrator policy below, or the guarantee breaks one level down.
- Add explicit denies for what resource policies could otherwise open: because some resource-based grants bypass a boundary's implicit deny, add explicit Deny statements for the most sensitive actions, such as reading the security team's secrets or changing CloudTrail.
2. The delegated administrator policy
| Statement purpose | Actions | Control |
|---|---|---|
| Create and change roles only with the boundary | iam:CreateRole, iam:PutRolePolicy, iam:AttachRolePolicy, iam:DetachRolePolicy, iam:DeleteRolePolicy, iam:PutRolePermissionsBoundary | Allow only when iam:PermissionsBoundary equals the boundary ARN (the aws-samples policy expresses this as a Deny when the condition does not match) |
| Keep the boundary on | iam:DeleteRolePermissionsBoundary | Deny on all resources |
| Keep the boundary policy unchanged | iam:CreatePolicyVersion, iam:DeletePolicyVersion, iam:SetDefaultPolicyVersion, iam:DeletePolicy | Deny on the boundary policy ARN and on the delegated administrator policy ARN |
| Work only in the delegated namespace | Role and policy create, update, delete, tag, UpdateAssumeRolePolicy | Resources limited to role/applicationroles/* and policy/applicationpolicies/*, so platform and break-glass roles cannot be touched |
| Pass only delegated roles | iam:PassRole | Resource role/applicationroles/*, with iam:PassedToService listing the services the team uses |
| Instance profiles | Create, add role, remove role, delete instance profile | Resources in the delegated instance-profile path |
| No long-term users | iam:CreateUser, iam:CreateAccessKey | Not granted; IAM users only by exception. See the AWS IAM Roles vs IAM Users guide |
AWS's own delegation example in the IAM User Guide follows the same structure with statements named CreateOrChangeOnlyWithBoundary (allow user and policy changes only when iam:PermissionsBoundary matches the boundary), NoBoundaryPolicyEdit (deny version changes and deletion of both the boundary and the delegated administrator's own boundary policy) and NoBoundaryUserDelete (deny iam:DeleteUserPermissionsBoundary). Note that AWS's example also protects the policy attached as the delegated administrator's own boundary: if the delegate could edit that, every other guard falls.
3. The SCP that protects the platform
Deny changes to platform roles (for example role/platform/* and break-glass roles) for everyone except the platform pipeline role, using aws:PrincipalArn conditions. This catches the case where a delegated policy is mis-scoped in one account. See the AWS Control Tower Guide for applying it across accounts.
Privilege escalation paths the pattern must close
| Escalation path | Without the guard | Guard |
|---|---|---|
| Create a role with no boundary | Developer creates an admin role and assumes it | iam:PermissionsBoundary condition on CreateRole |
| Remove the boundary later | Bounded role becomes unbounded | Deny iam:DeleteRolePermissionsBoundary |
| Swap to a weaker boundary | PutRolePermissionsBoundary with a permissive policy | Same iam:PermissionsBoundary condition on PutRolePermissionsBoundary |
| Edit the boundary policy | New default version allows iam:* for every bounded role | Deny CreatePolicyVersion and SetDefaultPolicyVersion on the boundary ARN |
| Change an existing privileged role | Attach a policy to, or rewrite the trust policy of, a platform role | Path-scoped resources and the platform SCP |
| Pass a privileged role to a service | Launch a Lambda function or EC2 instance with an admin role the developer did not create | iam:PassRole limited to the delegated path and iam:PassedToService |
| Resource policy grant to a session or user ARN | Bucket, key or secret policy grants a role session directly, outside the boundary | Explicit denies in the boundary for sensitive actions; review resource policies with IAM Access Analyzer |
In ATT&CK these paths are T1098.003 Account Manipulation: Additional Cloud Roles and abuse of T1078.004 Valid Accounts: Cloud Accounts. For a wider method to find such paths, see How to Find IAM Privilege Escalation Paths in AWS.
Choosing the approach for your organisation
- Central IAM team creates every role: fits small organisations with few accounts and slow change. No boundary is needed for delegation, but the team becomes a bottleneck and often grants broad roles to keep up.
- Delegated creation with boundaries: fits product teams that own their own accounts and ship daily. The boundary caps blast radius; the platform team owns two or three boundaries and the delegated administrator policy.
- Pipeline-only creation: fits regulated estates. Humans cannot create roles at all; the CI/CD deployment role holds the delegated administrator policy and roles come from reviewed infrastructure as code. The boundary still matters, because the pipeline runs whatever the template asks for. See the AWS CloudFormation and CDK Security Guide.
- Account per workload: in a well-separated multi-account estate, SCPs and account boundaries do much of the work, and permissions boundaries become a second layer for shared or platform accounts. See the AWS Multi-Account Security Reference Architecture Guide.
Trade-offs: tight boundaries generate support requests when teams need a new service; broad boundaries cap little. Start with one boundary per business function, review its actions against IAM Access Analyzer last-accessed data each quarter, and add services by change request.
Testing and rollout
- Write the boundary and delegated policies as code, in a sandbox account, with the paths and ARNs your organisation will use.
- Validate the policies with IAM Access Analyzer policy validation and custom policy checks before attaching them. See the IAM Access Analyzer Guide.
- Run negative tests as the delegated principal: create a role without a boundary, create one with another boundary, delete the boundary, put a new boundary policy version, attach AdministratorAccess to a platform role, update a platform role's trust policy and pass a platform role to Lambda. Every call must return AccessDenied; capture the CloudTrail records as evidence.
- Run positive tests: create a workload role with the boundary, attach a scoped policy and deploy a function that uses it, confirming the intended service actions succeed.
- Pilot with one team in one account, keep the previous central process available, and track AccessDenied errors for two to four weeks. The AWS AccessDenied Troubleshooting Guide explains how the error context identifies a boundary denial.
- Backfill existing roles: attach the boundary to roles in the delegated path that were created earlier, after checking last-accessed data so nothing in use is cut off.
- Roll out by OU and add the platform SCP. Rollback is detaching the delegated administrator policy and restoring the central process; boundaries on roles can stay, since they only restrict.
Detection and measurement
| CloudTrail event | Alert when |
|---|---|
| CreateRole, CreateUser | The request has no permissionsBoundary, or a boundary other than the approved ARNs |
| DeleteRolePermissionsBoundary, DeleteUserPermissionsBoundary | Any occurrence outside the platform pipeline role |
| PutRolePermissionsBoundary, PutUserPermissionsBoundary | The new boundary is not an approved ARN |
| CreatePolicyVersion, SetDefaultPolicyVersion | The target is a boundary or delegated administrator policy |
| AttachRolePolicy, PutRolePolicy, UpdateAssumeRolePolicy | The target role is outside the delegated path |
| PassRole denials (AccessDenied on service create calls) | Repeated attempts by one principal, which suggest probing |
AWS Config can evaluate roles continuously; a custom rule that flags roles in delegated paths without the approved boundary closes the gap for roles created before the pattern. See the AWS Config Guide and the AWS CloudTrail Guide.
Metrics:
- Percentage of roles in delegated paths carrying an approved boundary (target 100).
- Number of approved boundary policies and their last review date.
- Boundary removal or replacement attempts per month, and the share blocked.
- Median time from role request to role in production, before and after delegation.
- Unused actions in each boundary per quarter, from last-accessed data.
Common failure patterns
- Only the create condition: CreateRole requires the boundary, but DeleteRolePermissionsBoundary or PutRolePermissionsBoundary is not guarded, so the boundary can be removed or swapped the next day.
- Boundary policy editable by the delegate: the delegated administrator can publish a new default version of the boundary, which silently expands every bounded role.
- Unfenced PassRole: iam:PassRole on * lets a developer launch compute with an existing administrator role and never touch the boundary at all.
- Boundary includes iam:*: bounded roles can create new, unbounded roles, so the cap does not hold one level down.
- Relying on the boundary against resource policies: a bucket, key or secret policy grants a role session or IAM user directly, outside the boundary's implicit deny.
- Boundary used instead of least privilege: workload policies are left as *:* because "the boundary caps it", so every role in a team has the full boundary. See the Least Privilege Techniques Guide.
Framework mapping
| Control area | NIST SP 800-53 Rev 5 | NIST CSF 2.0 | ISO/IEC 27001:2022 Annex A | CIS Controls v8.1 |
|---|---|---|---|---|
| Maximum permissions for delegated roles | AC-6, AC-6(1) | PR.AA-05 | A.5.15, A.8.2 | 6 Access Control Management |
| Separation of duties for boundary owners | AC-5 | PR.AA-05 | A.5.3 | 6 |
| Account and role lifecycle | AC-2 | PR.AA-01 | A.5.16, A.5.18 | 5 Account Management |
| Change restriction on IAM policies | CM-5 | PR.PS-01 | A.8.32 | 4 Secure Configuration |
| Logging of IAM changes | AU-2, AU-12 | PR.PS-04, DE.CM-09 | A.8.15, A.8.16 | 8 Audit Log Management |
Practitioner checklist
- Write one boundary per business function with Allow-only service actions, no IAM, and explicit denies for the most sensitive actions.
- Require iam:PermissionsBoundary equal to an approved ARN on CreateRole, PutRolePermissionsBoundary and role policy changes.
- Deny DeleteRolePermissionsBoundary and all version changes on the boundary and delegated administrator policies.
- Limit delegated role and policy actions to a dedicated path, and fence iam:PassRole to that path with iam:PassedToService.
- Protect platform and break-glass roles with an SCP keyed on aws:PrincipalArn.
- Run the negative test set and keep the CloudTrail evidence before each rollout.
- Review resource-based policies that grant user or session ARNs, since boundaries do not cap them.
- Alert on boundary removal, replacement and boundary policy edits, and flag unbounded roles in delegated paths with AWS Config.
Standards and references
- AWS: Permissions boundaries for IAM entities
- AWS: Policy evaluation logic, explicit and implicit denies
- AWS: IAM and AWS STS condition context keys
- AWS Security Blog: When and where to use IAM permissions boundaries
- AWS Samples: example-permissions-boundary
- NIST: SP 800-53 Rev. 5 Security and Privacy Controls
Related NHI Mgmt Group resources: AWS Organizations and SCPs Guide · AWS IAM Policies and Roles Guide · How to Find IAM Privilege Escalation Paths in AWS · Cloud PAM and CIEM Guide