All writing

SCPs, permission boundaries and IAM policies: who actually wins?

AWS authorization is not a contest between policy documents. Identity and resource policies provide grant paths, permissions boundaries limit individual IAM entities, SCPs and RCPs impose organisational ceilings, and applicable explicit denies determine the result.

about 18 minutes min read

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

  1. AWS IAM: Policy evaluation logic
  2. AWS IAM: How AWS evaluates requests
  3. AWS IAM: Permissions boundaries
  4. AWS IAM: Identity-based and resource-based policies
  5. AWS IAM: Principal policy element
  6. AWS IAM: NotPrincipal policy element
  7. AWS IAM: IAM roles and trust policies
  8. AWS IAM: Passing roles to AWS services
  9. AWS IAM: IAM and STS condition keys
  10. AWS IAM: Cross-account policy evaluation
  11. AWS Organizations: SCP evaluation
  12. AWS Organizations: Service Control Policies
  13. AWS Organizations: Resource Control Policies
  14. AWS Organizations: RCP evaluation
  15. AWS IAM: Troubleshooting access denied