IAM answers every request the same way, in the same order. Knowing the order turns AccessDenied from a wall into a message.
The rules
- Start from deny. A request nobody has allowed is denied; this is the implicit deny.
- Gather every policy that applies: the user's inline and attached policies, the policies of every group the user is in, and any resource-based policy on the target.
- If any statement explicitly denies the action on the resource, the answer is deny. Nothing can override it.
- Otherwise, if any statement allows it, the answer is allow.
- Otherwise, implicit deny.
A statement applies only when its action pattern matches the action and its resource pattern matches the resource, and its conditions hold. A policy with a typo in a resource ARN applies to nothing. It does not error; it simply never grants.
Reading the message
User: arn:aws:iam::123456789012:user/sam is not authorized to perform: s3:ListBucket on resource: "arn:aws:s3:::reports" because no identity-based policy allows the s3:ListBucket actionUser: arn:aws:iam::123456789012:user/lee is not authorized to perform: s3:ListBucket on resource: "arn:aws:s3:::reports" with an explicit deny in an identity-based policyThe first is implicit: add an Allow. The second is explicit: find the Deny and decide whether it should stay. Same error code, opposite fixes.
Asking before trying
aws iam simulate-principal-policy \
--policy-source-arn arn:aws:iam::123456789012:user/sam \
--action-names s3:ListBucket s3:GetObject \
--resource-arns arn:aws:s3:::reports arn:aws:s3:::reports/q3.txtThe policy simulator evaluates a principal's policies against actions and resources and answers allowed, implicitDeny or explicitDeny for each pair, without touching anything. It is how you check a policy before handing it to a person.