Conditions & evaluation

How AWS evaluates policies (and why explicit deny wins)

Every request starts as an implicit deny; one explicit deny anywhere ends evaluation. Walk through the evaluation chain and the cross-account rule that both accounts must allow.

Published · Updated .

When an IAM role's request is denied, the instinct is to add another Allow. That fails whenever the cause is an explicit deny, a guardrail or a missing grant on the other side of an account boundary. Knowing the order AWS evaluates policies in turns “why is this denied?” from guesswork into a checklist.

Two rules that never change

  • Default deny. Every request starts as an implicit deny. Only the account root user starts with full access, and even the root user of a member account is limited by SCPs.
  • Explicit deny wins. If any applicable policy contains a Deny statement that matches the request, the request is denied. No Allow can override it.

The evaluation sequence (single account)

AWS's enforcement code checks applicable policies in this order:

  1. Explicit deny check across all applicable policies: SCPs, RCPs, resource-based policies, identity-based policies, permissions boundaries and session policies. Any match: Deny.
  2. Resource control policies (RCPs) on the resource's account. If they do not allow the action: Deny. (When RCPs are enabled, AWS attaches RCPFullAWSAccess, which cannot be detached.)
  3. Service control policies (SCPs) on the principal's account. If they do not allow the action: Deny.
  4. Resource-based policies. In the same account, an Allow that names the IAM user, or the role session making the request, can produce a final Allow even if identity policies, boundaries or session policies would implicitly deny. An Allow naming the role ARN is still subject to the role's boundary and session policies.
  5. Identity-based policies. If none allows the action: Deny.
  6. Permissions boundary, if the user or role has one. If it does not allow the action: Deny.
  7. Session policies, if the request comes from a session created with one. If it does not allow the action: Deny. Otherwise: Allow.

Cross-account requests

When the principal and the resource are in different accounts, the request needs an Allow from the resource side (a resource-based policy naming the principal, or a role the principal can assume) and an Allow from the principal's own account (identity policy, within its boundary and SCPs). Guardrails on both sides apply: the caller's SCPs and the resource owner's RCPs.

Worked scenarios

Evaluation outcomes for common scenarios
ScenarioResultWhy
Identity policy allows s3:GetObject; nothing else appliesAllowAn explicit Allow and no Deny anywhere.
Identity policy allows; bucket policy denies non-TLS; request over HTTPDenyOne explicit Deny ends evaluation, whatever else allows.
Identity policy allows; the account's SCPs do not allow the actionDenySCPs set the ceiling; an action not allowed by every SCP level is denied.
Role's identity policy allows; its permissions boundary does not include the actionDenyIdentity policy and boundary must both allow (implicit deny in the boundary).
Same account: bucket policy grants the role session ARN; no identity policy allowsAllowA resource-based grant to the session principal is not limited by implicit denies.
Cross-account: bucket policy grants an external role; that role has no identity policy for S3DenyAcross accounts, both accounts must allow.

The second scenario uses a guardrail like this one. It denies everyone, but only for plain-HTTP requests:

Bucket policy: baseline denies · Amazon S3 bucket policyOpen in S3 Bucket Policy Generator
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyInsecureTransport",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "s3:*",
      "Resource": [
        "arn:aws:s3:::amzn-s3-demo-bucket",
        "arn:aws:s3:::amzn-s3-demo-bucket/*"
      ],
      "Condition": {
        "Bool": {
          "aws:SecureTransport": "false"
        }
      }
    }
  ]
}

A debugging checklist

  1. Read the error. AWS access-denied messages name the type of policy responsible, for example “with an explicit deny in a service control policy” or “because no identity-based policy allows the … action”. That points straight at the step above that failed.
  2. Check CloudTrail for the denied event: the userIdentity shows which principal and session actually made the call. It is often not the one you expected.
  3. Simulate identity policies with aws iam simulate-principal-policy or simulate-custom-policy, supplying context keys for conditions. Check the simulator documentation for which policy types it includes for your scenario.
  4. Look outside the account for cross-account and organisation-level policies, which account-level administrators often cannot see.

Related: identity vs. resource-based policies and boundaries vs. SCPs.

Official AWS documentation