Guide

When a principal calls a cloud API, the cloud has to answer yes or no. It does that by collecting every policy that applies, then following a fixed set of rules in a fixed order. Once you know those rules, most "why was I denied?" questions answer themselves.

Concept

A principal is anything that can authenticate: a user, a machine role, a service account, or a federated login from an identity provider. A policy is a document that allows or denies actions on resources. A role is a named set of permissions that a principal can assume for a limited time.

The big idea

There are two shapes of policy, and both can matter.

ShapeAttached toTypical use
Identity-basedA user, group, role or service account"This role may read objects in these buckets"
Resource-basedThe resource itself: a bucket, queue, key or function"This bucket accepts writes from that role"

Within one account, an allow from either side can grant access. Across accounts, both sides normally have to allow. GCP leans more on identity bindings and less on separate resource policies, but the question is the same: identity, action, resource, and conditions.

Walking through a request

When a principal calls an API, AWS roughly does this:

  1. Authenticates the caller.
  2. Collects every applicable policy: identity, resource, permission boundaries, Service Control Policies (SCPs) and session policies.
  3. Applies explicit deny. Any deny anywhere blocks the call.
  4. Otherwise requires at least one allow. No allow means deny.

Here is a trace for one role reading one bucket:

StepWhat the policies sayResult
1Identity policy allows s3:GetObject on the invoices bucketAn allow exists
2Bucket policy has no matching denyKeep going
3An SCP above the account does not allow s3:GetObjectCall denied
4Same SCP, but it now allows the actionCall allowed

Permission boundaries and SCPs only subtract power. They are ceilings, not grants. Adding an allow to the role does nothing if an SCP above it blocks the action.

That is why "I attached AdministratorAccess" works instantly, and why a missing resource ARN fails silently until you read CloudTrail.

Common mistakes

  • Looking for missing allows first. Check for explicit denies, SCPs and boundaries before you add anything.
  • Treating an SCP as a grant. SCPs never give access. They only cap what member accounts can allow.
  • Getting the resource ARN wrong. The policy looks right, the call still fails, and the only clue is in the audit log.
  • Assuming the policy you attached is the one evaluated. Resource policies and boundaries count too.

Check yourself

  1. A role's identity policy allows s3:GetObject, but an SCP above the account does not allow it. Is the call allowed? No. An SCP is a ceiling, so an allow inside the account cannot go past it.
  2. A bucket policy explicitly denies a role, but the role's identity policy allows the same action. What happens? Denied. An explicit deny wins over any allow.

Key takeaways

  • Explicit deny always wins.
  • A request needs at least one allow, or it is denied by default.
  • SCPs and permission boundaries are ceilings. They limit power and never grant it.
  • Debug denials in order: principal, action, resource, then denies, then missing allows.

Further learning

Next, see how to shrink these policies safely in least privilege in practice.