An IAM role has an identity policy allowing:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:*",
"Resource": "*"
}
]
}
The role appears to have complete Amazon S3 access.
It also has a permissions boundary allowing only:
s3:GetObject
s3:ListBucket
The AWS account belongs to an OU whose applicable SCP path explicitly denies:
s3:DeleteBucket
The role attempts:
s3:GetObject
s3:PutObject
s3:DeleteBucket
Which policy wins?
The tempting answer is:
SCP beats permissions boundary.
Permissions boundary beats IAM policy.
That sounds like a hierarchy.
It is not how AWS authorization works.
For s3:GetObject:
identity policy:
allows
permissions boundary:
allows
applicable SCP path:
leaves action available
result:
may be allowed
subject to resource and request conditions
For s3:PutObject:
identity policy:
allows
permissions boundary:
no applicable allow
result:
denied
For s3:DeleteBucket:
identity policy:
allows
permissions boundary:
no applicable allow
SCP:
explicit deny
result:
denied
The policies have not fought a duel.
They have answered different questions about one request.
An SCP limits authority available to affected member-account principals. A permissions boundary limits what identity policies may grant to one IAM user or role. Identity and resource policies provide grant paths. An applicable explicit deny determines the result.
These are different authorization contracts.
Their relationship depends on the request and the principal represented in it.
Different policies answer different questions
AWS evaluates a request, not a stack of documents.
The request includes:
- principal
- action
- resource
- account
- Region
- session
- tags
- network context
- service-specific conditions
The relevant policy families answer different questions.
ORGANISATION CONTRACT
SCP:
which authority may remain available
to affected member-account principals?
RCP:
which authority may be exercised
against supported member-account resources?
ENTITY CONTRACT
permissions boundary:
which authority may identity policies
grant to this IAM user or role?
GRANT CONTRACT
identity or resource policy:
where does permission come from?
SESSION CONTRACT
session policy:
how much of the identity's authority
may this temporary session use?
ASSUMPTION CONTRACT
trust policy:
who may create the role session?
AWS starts from default denial.
The compact decision is:
APPLICABLE EXPLICIT DENY?
yes
│
▼
DENY
no
│
▼
VALID GRANT EXISTS
AND REQUIRED CEILINGS
LEAVE IT AVAILABLE?
yes ──► ALLOW
no ──► DENY
A missing grant can deny a request.
A missing allow in a permissions boundary, session policy or SCP allow-list path can deny it.
An explicit deny can also determine the result.
AdministratorAccess is broad.
It is not a royal pardon.
Permissions boundaries are identity ceilings
A permissions boundary is a managed policy attached as the boundary of an IAM user or role.
It defines the maximum permissions that identity-based policies may grant to that entity.
IDENTITY POLICY
proposes authority
│
▼
PERMISSIONS BOUNDARY
limits what may become effective
│
▼
EFFECTIVE IDENTITY PERMISSION
Suppose a workload role has an identity policy allowing:
s3:GetObject
s3:PutObject
s3:DeleteObject
Its boundary allows:
s3:GetObject
s3:PutObject
The effective identity-policy authority is:
s3:GetObject
s3:PutObject
The delete permission remains outside the boundary.
Adding AdministratorAccess does not lift the ceiling.
It merely gives the boundary more proposed permissions to trim.
A ceiling is not a grant.
If the boundary allows S3 but the role has no S3 grant, the role still has no S3 authority.
Give the boundary an operating contract
A boundary is a shared control dependency.
Changing it may expand or reduce the maximum identity-policy authority of every attached role.
boundary:
name: bfstore-workload-boundary
owner: platform-iam
eligible_roles:
paths:
- /workloads/
- /controllers/
allows:
- approved application services
- telemetry
- scoped resource access
prohibits:
- organizations administration
- unrestricted IAM
- boundary removal
- uncontrolled role passing
protected_from:
- delegated policy-version changes
- deletion
- replacement
tests:
- catalog-role-can-read-assets
- catalog-role-cannot-create-users
- creator-cannot-remove-boundary
- creator-cannot-pass-admin-role
- direct-session-resource-grant-detected
last_reviewed: 2026-03-01
Test both sides:
CREATED ROLE
cannot exceed the boundary
CREATOR ROLE
cannot remove, rewrite
or route around the boundary
The second test is often more important.
Trust is a separate authorization event
Role assumption and the later service operation are two requests.
Event one: create the session
sts:AssumeRole
│
├── caller authority
├── role trust policy
├── assumption conditions
└── optional session policy
│
▼
temporary credentials
The trust policy answers:
Who may become this role?
Event two: use the session
s3:GetObject
│
├── identity or resource grant
├── permissions boundary
├── session policy
├── SCP and RCP
└── resource conditions
│
▼
service authorization
Once assumption succeeds, the trust policy is not normally re-evaluated for every subsequent S3 request.
A role can have an excellent boundary and a dangerous trust policy.
The boundary limits the authority of the resulting session.
It does not make arbitrary role assumption sensible.
Trust and permission are connected.
They are not interchangeable.
Resource policies create alternate grant paths
An identity-based policy is the usual grant path for an IAM user or role.
A supported resource-based policy may provide another path.
The principal named by that resource policy changes the result.
Same-account principal behaviour
| Principal named by the resource policy | Effect of an implicit boundary or session denial |
|---|---|
| IAM role ARN | The role remains limited |
| Assumed-role session ARN | The direct session grant is not limited |
| IAM user ARN | The direct user grant is not limited by an implicit identity-policy or boundary denial |
| STS federated-user session ARN | The direct session grant is not limited |
| IAM user ARN for the user who created a federated session | Boundary and session limits still matter |
Applicable explicit denies still determine the result.
SCPs and RCPs remain relevant where they apply.
Role ARN and role-session ARN are different
A bucket policy granting to:
arn:aws:iam::111122223333:role/catalog-assets-reader
grants to the IAM role.
An implicit deny in its permissions boundary or session policy can still limit the request.
A grant directly to:
arn:aws:sts::111122223333:
assumed-role/catalog-assets-reader/session-name
is a direct grant to the role session.
Within the same account, that grant is not limited by an implicit absence of allow in an identity policy, permissions boundary or session policy.
An applicable explicit deny still blocks it.
This is not a reason to grant casually to session ARNs.
It is a reason not to say:
the boundary always beats
the bucket policy
The aws:PrincipalArn wildcard pattern
Another subtle pattern uses:
"Principal": "*"
with a condition on:
aws:PrincipalArn
When this pattern grants to the effective role principal, permissions boundaries and session policies may not limit the grant through implicit denial in the way an ordinary role-ARN principal grant does.
An explicit deny can still block it.
The lesson is practical:
A permissions boundary cannot be reviewed in isolation from resource policies that may create a direct grant to the effective principal.
Explicit denial is different
These nuances concern implicit denial, such as a boundary containing no applicable allow.
An applicable explicit deny remains decisive.
A resource-policy allow cannot cancel an explicit deny in:
- an identity policy
- a permissions boundary
- a session policy
- an SCP
- an RCP
- another applicable resource policy
Avoid the NotPrincipal boundary trap
AWS warns against combining:
Effect: Deny
NotPrincipal: ...
with IAM users or roles that have permissions boundaries.
That combination can deny bounded IAM principals regardless of the values listed in NotPrincipal.
AWS recommends a deny statement using an ARN condition instead.
{
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::bfstore-protected",
"arn:aws:s3:::bfstore-protected/*"
],
"Condition": {
"ArnNotEquals": {
"aws:PrincipalArn":
"arn:aws:iam::111122223333:role/security/ApprovedOperator"
}
}
}
This remains illustrative.
The exact resource semantics and exception path need service-specific testing.
The more useful question is not:
Which policy wins?
It is:
Which statement applies
to this exact principal form
and request context?
Delegate IAM without delegating escape
Permissions boundaries are useful when platform engineers may create workload roles but must not create identities beyond an agreed ceiling.
A boundary requirement alone is insufficient.
A delegated creator must not be able to remove, rewrite or route around the control.
Require the boundary
Role creation and updates should require the approved boundary ARN.
iam:PermissionsBoundary
must equal the governed
boundary policy
Prevent removal or replacement
The delegated administrator must not be able to remove the boundary or replace it with another policy.
Protect the boundary policy
A customer-managed boundary has policy versions.
An administrator able to create and select a more permissive default version can change every attached role’s ceiling.
The boundary document needs protected ownership.
Govern trust policies
The creator should not be able to establish arbitrary trust relationships.
Allowed principals should be limited to approved workloads, repositories, accounts and AWS services.
Restrict iam:PassRole
A principal with iam:PassRole may configure an AWS service to use a role whose authority exceeds the principal’s own.
Restrict:
- exact role paths
- exact account
- approved target services
- approved creation workflows
The iam:PassedToService condition key can constrain which service receives the role.
Review resource-policy bypasses
A delegated administrator may route around an implicit boundary ceiling by creating a resource policy that grants directly to a user or session principal.
Resource-policy creation and changes need conformance controls where that path exists.
A delegated model might be:
delegated_iam:
creator: bfstore-platform-role
may_create:
role_paths:
- /workloads/
- /controllers/
required_boundary:
arn: arn:aws:iam::111122223333:policy/bfstore-workload-boundary
trust:
approved_principals:
- approved EKS workload identities
- approved AWS services
pass_role:
role_paths:
- /workloads/
- /controllers/
approved_services:
- eks.amazonaws.com
- lambda.amazonaws.com
prohibited:
- remove required boundary
- modify boundary policy
- create arbitrary trust
- pass administrative roles
- modify Identity Center roles
- create direct session grants
A permissions boundary makes delegated IAM safer only when the administrator cannot remove, rewrite or route around it.
Organisation controls remain outside the entity boundary
SCPs and permissions boundaries are both ceilings.
They operate at different scopes.
| Property | SCP | Permissions boundary |
|---|---|---|
| Attached to | Root, OU or member account | IAM user or role |
| Primary owner | Organisation governance | IAM or platform governance |
| Scope | Affected member-account principals | One IAM entity |
| Grants permission | No | No |
| Constrains management-account principals | No | Yes, when attached |
| Constrains member-account root | Yes | Cannot attach to root |
| Applies to service-linked roles | No | Cannot attach to service-linked roles |
An SCP path must leave the action available.
Under an allow-list strategy, appropriate allow coverage must exist at every applicable level.
Under a deny-based strategy, the inherited baseline must still preserve availability and no applicable deny may block the request.
RCPs govern the resource side
RCPs constrain access to supported resources in member accounts.
They apply regardless of whether the requesting principal belongs to the same organisation.
They do not grant access.
They also have documented exclusions:
- only supported services and resource types participate
- management-account resources are not constrained
- service-linked-role calls are not constrained
- selected service-specific resources or actions may be excluded
SCP
principal-side organisation ceiling
RCP
supported resource-side
organisation ceiling
The controls complement one another.
They are not duplicates.
Cross-account access spans two policy environments
Suppose a role in bfstore-shared-services reads a bucket in bfstore-prod.
The request crosses two administrative environments.
TRUSTED PRINCIPAL SIDE
identity permission
permissions boundary
session policy
principal-account SCP
│
▼
CROSS-ACCOUNT REQUEST
│
▼
TRUSTING RESOURCE SIDE
bucket policy
resource-account RCP
resource conditions
service-specific controls
The principal-side SCP limits the caller.
The resource-owning account’s SCP does not constrain an external caller merely because the resource lives there.
The resource-side RCP can constrain access to a supported member-account resource.
Cross-account access is a handshake performed inside two policy environments.
The useful questions are:
Does the principal side
permit the request?
Does the resource side
trust the principal?
Does the principal-side SCP
leave the authority available?
Does the resource-side RCP
leave the resource able to accept it?
Does any applicable policy
explicitly deny it?
A bfstore workload-role contract
Suppose catalog-service needs to read product assets.
role:
name: catalog-assets-reader
path: /workloads/catalog/
owner: catalog-platform
trust:
principal: approved EKS workload identity
cluster: bfstore-prod
namespace: catalog
service_account: catalog-service
permissions:
allow:
- action: s3:GetObject
resource: arn:aws:s3:::bfstore-product-assets/catalog/*
boundary:
name: bfstore-workload-boundary
organisation:
scp_scope: Workloads/Production
expected_availability:
- s3:GetObject
prohibited:
- iam administration
- organisations administration
resource_controls:
bucket_policy:
- require secure transport
- permit approved workload path
rcp:
- preserve approved organisation access
tests:
- workload-can-read-catalog-assets
- workload-cannot-write-assets
- workload-cannot-create-iam-user
- creator-cannot-remove-boundary
- near-match-workload-cannot-assume-role
The request succeeds only when:
approved workload obtains the session
identity or resource policy grants GetObject
permissions boundary leaves GetObject available
SCP path leaves GetObject available
RCP leaves the resource able to accept it
bucket policy and request conditions pass
no applicable explicit deny exists
This is not one policy winning.
It is delegated authority enclosed by several deliberate contracts.
Diagnose one precise request
Start by identifying the failed event.
Did AssumeRole fail?
or
Did a later service request fail?
If role assumption failed
Inspect:
- caller identity
- caller permission for
sts:AssumeRole - role trust policy
- trust conditions
- caller boundary
- caller session policy
- caller-account SCP
If a service request failed
Inspect:
- exact session principal
- action
- resource
- Region and conditions
- identity or resource grant
- permissions boundary
- session policy
- principal-side SCP
- resource-side RCP
- resource-policy principal form
- service-specific controls
Read the full error message.
AWS may identify an implicit or explicit denial and the policy type involved.
That message is useful evidence.
It is not necessarily a complete account of every denial. When several policy types deny the same request, AWS may report only one.
The question is:
Which required grant is missing?
Which ceiling does not
leave the action available?
Which applicable explicit deny
determines the result?
Questions I now ask
Event
Did role assumption fail,
or a later service request?
Principal
Which exact principal form
made the request?
Grant
Which identity or resource policy
supplies the allow?
Entity limits
Which permissions boundary
or session policy applies?
Organisation limits
Which SCP constrains the principal
and which RCP constrains the resource?
Resource principal
Does the resource policy name
a user, role, session or account?
Decision
Is there an explicit deny
or a missing required allow?
Delegation
Can the IAM administrator remove,
rewrite or bypass the ceiling?
These questions are more useful than ranking policy names by prestige.
The mental model I am keeping
My earlier model was:
SCP
beats
permissions boundary
beats
IAM policy
The stronger model is:
AWS REQUEST
│
▼
PRINCIPAL FORM
│
┌────────────────┼────────────────┐
│ │ │
▼ ▼ ▼
GRANT PATH ENTITY LIMITS ORGANISATION LIMITS
│ │ │
├── identity ├── boundary ├── SCP
└── resource └── session └── RCP
│ │ │
└────────────────┼────────────────┘
▼
APPLICABLE EXPLICIT DENY?
│
┌──────────┴──────────┐
│ │
yes no
│ │
▼ ▼
DENY VALID GRANT AND ALL
REQUIRED AVAILABILITY?
│
┌──────────┴──────────┐
│ │
yes no
│ │
▼ ▼
ALLOW DENY
The identity or resource policy answers:
Where does authority come from?
The permissions boundary answers:
How much authority may identity policies
grant to this user or role?
The session policy answers:
How much authority may
this temporary session use?
The SCP answers:
Which authority may remain available
to affected member-account principals?
The RCP answers:
Which authority may be exercised
against supported member-account resources?
The trust policy answers:
Who may create the role session?
And an applicable explicit deny answers:
Must this precise request fail?
No policy wins because it has a more serious name or sits higher in the console.
The architecture succeeds when grant paths, entity ceilings, organisation guardrails, trust relationships and resource controls are designed together.
When that system is designed well:
- organisation governance owns SCPs and RCPs
- IAM governance owns permissions boundaries
- service owners own workload grants
- identity owners manage trust
- resource owners manage resource policies
- platform engineering tests the complete path
When it is designed badly, every denial produces the same ritual:
Attach a broader IAM policy
and see whether the error goes away.
Sometimes the missing permission was never the problem.
And sometimes AdministratorAccess arrives at the guardrail, adjusts its tie and discovers that the gate remains very firmly closed.
References and further reading
- AWS IAM: Policy evaluation logic
- AWS IAM: How AWS evaluates requests
- AWS IAM: Permissions boundaries
- AWS IAM: Identity-based and resource-based policies
- AWS IAM: Principal policy element
- AWS IAM: NotPrincipal policy element
- AWS IAM: IAM roles and trust policies
- AWS IAM: Passing roles to AWS services
- AWS IAM: IAM and STS condition keys
- AWS IAM: Cross-account policy evaluation
- AWS Organizations: SCP evaluation
- AWS Organizations: Service Control Policies
- AWS Organizations: Resource Control Policies
- AWS Organizations: RCP evaluation
- AWS IAM: Troubleshooting access denied