AWS IAM Policy Evaluation
AWS IAM’s policy evaluation engine decides whether a particular principal may perform a particular action on a particular resource. It gathers the policies that apply to the request, looks for an applicable explicit deny, and then determines whether an applicable allow remains.
This is the mechanism behind familiar errors such as AccessDenied and not authorized to perform. Understanding it means treating permissions as a calculation, not as a single list of grants.
The problem IAM is solving
A cloud request can be made by a user, an application using a role, or an AWS service. Different teams may control different parts of the authorization decision:
- An identity owner can define what a role is generally allowed to do.
- A resource owner can decide who may access a particular bucket, queue, key, or topic.
- A security team can impose a maximum permission set on a role.
- A workload operator can further restrict one temporary session.
Without a common evaluation model, these controls could accidentally cancel one another or let a broad grant bypass a security restriction. IAM instead combines them with a default-deny rule and a universal override: an applicable explicit deny wins over an allow.
Start with one request
Consider an application using a role to call s3:GetObject for one object. IAM evaluates facts about that exact request:
- Principal: the identity making the request, such as a role session.
- Action: the operation, such as
s3:GetObject. - Resource: the object ARN.
- Conditions: attributes such as the source IP, requested encryption, or organization.
A policy statement applies only if its action, resource, principal, and conditions match those facts. A statement mentioning s3:GetObject does not help with s3:PutObject, and a statement for one bucket does not automatically apply to another.
The engine then follows this simplified model:
- Start with implicit deny: the request is denied unless an applicable allow is found.
- If any applicable statement explicitly says
"Effect": "Deny", deny the request. - Otherwise, allow only if the relevant policy combination contains an applicable allow.
An implicit deny is simply the absence of permission. An explicit deny is a matching policy statement that says to deny. They produce the same immediate result, but an explicit deny also defeats matching allows.
The four policy types
Identity-based policies: what an identity can request
An identity-based policy is attached to a user, group, or role. It says what actions that identity may perform on which resources. For example:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::reports-prod/*"
}
]
}
This grants a matching identity permission to read objects in reports-prod; it does not grant permission to write them.
Resource-based policies: who a resource accepts
A resource-based policy is attached to a resource. It names a principal—the user, role, account, or service that may use the resource—and describes the permitted action. S3 bucket policies and SQS queue policies are common examples.
Identity-based and resource-based policies generally combine as a union: an applicable allow in either policy category can provide the permission. This lets a resource owner grant access without editing every caller’s identity policy.
There is an important IAM detail for role sessions. A role is used by first creating a temporary role session, which has temporary credentials. If a resource policy grants directly to that session principal, that grant is not limited by the role’s identity policy, permissions boundary, or session policy. A grant to the role ARN itself is evaluated differently and remains subject to the role’s other limits. This distinction is one reason resource-policy principals must be chosen carefully.
Permissions boundaries: a ceiling on identity grants
A permissions boundary is a policy attached to a user or role that defines the maximum permissions identity-based policies can grant. It is not a grant by itself.
For an identity-based allow to take effect, it must be allowed both by the identity policy and by the boundary. Conceptually:
identity-based permissions ∩ boundary permissions
If the role policy allows s3:GetObject but the boundary does not, the role does not receive that permission through its identity policy. A boundary can therefore constrain delegated administration: a team may create or modify roles, but those roles cannot exceed the security team’s ceiling.
The boundary is not a universal filter on every possible resource-policy grant. In particular, the direct role-session grant described above has different behavior.
Session policies: a ceiling for one temporary session
A session policy is an additional policy supplied when temporary credentials are created, commonly when a role is assumed. It narrows that one session; it does not permanently change the role.
The role’s identity-based permissions and the session policy are intersected:
role identity permissions ∩ session policy permissions
If the role normally allows reads from two buckets but the session policy allows reads from only one, credentials from that session can read only one. A later session created without that restriction may have a different result, subject to the role and its boundary.
As with boundaries, a session policy is a limit, not an independent source of permission. A session policy that allows an action cannot make the action possible if the role itself does not allow it.
Why explicit deny wins
Suppose a role policy allows an application to read a bucket, while another applicable policy contains:
{
"Effect": "Deny",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::reports-prod/private/*"
}
A request for a private object may match both statements. IAM does not balance them or use the most recently written one. The deny removes the request from consideration immediately, so the result is denied.
This is useful for guardrails such as “never access this data from outside the corporate network” or “never delete these resources.” A broad allow can support normal operation, while a narrow explicit deny provides a safety boundary that other administrators cannot accidentally override with another allow.
The deny must be applicable: its action, resource, principal scope, and conditions must match the request. An unrelated deny elsewhere does not block every request in the account.
A practical mental model
For the four policy types in this article, reason in this order:
- Identify the actual principal, often a temporary role session.
- Find matching identity and resource allows.
- Apply the boundary and session-policy ceilings where they govern the grant.
- Search all applicable policies for an explicit deny.
- If no deny applies and a valid allow survives, IAM allows the request; otherwise the default is deny.
When a request fails, inspect the exact action, resource ARN, principal, conditions, and session used. An AccessDenied error usually means either that no matching allow survived the intersections or that one matching explicit deny overrode it—not that IAM selected a single policy and ignored the rest.