skip to content

In Amazon S3, how does a bucket policy differ from an IAM identity policy attached to a user or role, and which element does a bucket policy have that an identity policy never has?

level: juniorimportance: must knowfreq 78%

answer

  1. one attaches to whom, one to what
  2. which document names the caller
  3. union inside an account
  4. two ARN shapes for one bucket
  5. only one of them can say "*"

basics

~20 s

A bucket policy is a resource-based policy attached to the S3 bucket itself and must name a Principal. An IAM identity policy attaches to a user, group or role, and never names a Principal because the identity it is attached to is the principal.

solid answer

~40 s

Both are JSON policy documents with `Effect`, `Action`, `Resource` and optional `Condition`, but they hang off opposite ends of the request. An **identity policy** is attached to an IAM user, group or role and says "this principal may do X to these buckets" — it has no `Principal` element, because the attachment point is the principal. A **bucket policy** is attached to one bucket and says "these principals may do X to me", so `Principal` is required. Inside a single AWS account, an `Allow` in *either* one is enough, as long as nothing explicitly denies the call. Across accounts, you need an `Allow` on both sides. A bucket policy is also the only one of the two that can grant anonymous access, via `"Principal": "*"`, which is exactly why S3 Block Public Access exists.

code

json · 19 lines
json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ListTheBucket",
      "Effect": "Allow",
      "Principal": {"AWS": "arn:aws:iam::111122223333:role/ReportReader"},
      "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::reports-bucket"
    },
    {
      "Sid": "ReadTheObjects",
      "Effect": "Allow",
      "Principal": {"AWS": "arn:aws:iam::111122223333:role/ReportReader"},
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::reports-bucket/*"
    }
  ]
}

go deeper

for a junior

Be able to say plainly that a bucket policy lives on the bucket and names a Principal, while an identity policy lives on the user or role and does not. Know the two ARN shapes: the bucket for listing, bucket/* for objects.

for a middle

Explain the combination rules: inside one account an allow on either side suffices, across accounts both are required, and an explicit deny anywhere is final. Show why an over-broad bucket policy defeats careful IAM.

for a senior

Demonstrate judgment about where a rule belongs — guardrails and origin conditions on the resource, everyday grants on the identity — and be ready to debug an AccessDenied by naming which of the two documents was silent.

for a principal

Own the standard: which permissions are expressed centrally versus per-team, how bucket policies stay under their size limit as the estate grows, and how you keep a resource-policy sprawl auditable rather than accumulating one statement per consumer.

## Two policies, two ends of the same request Every S3 request has a caller (the principal) and a target (the bucket or object). AWS lets you write permissions from either end, and both kinds of document use the same grammar: a `Version`, a list of `Statement` objects, each with `Effect` (`Allow` or `Deny`), `Action`, `Resource`, and an optional `Condition` block. An **identity policy** is attached to an IAM user, group or role. It is a statement about what that identity may do: ```json { "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Action": ["s3:GetObject"], "Resource": "arn:aws:s3:::reports-bucket/*" }] } ``` There is no `Principal` element, and adding one is a syntax error — the principal is whoever the policy is attached to. A **bucket policy** is a resource-based policy attached to exactly one bucket. It is a statement about who may touch that bucket, so it *must* name the principal: ```json { "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Principal": {"AWS": "arn:aws:iam::111122223333:role/ReportReader"}, "Action": ["s3:GetObject"], "Resource": "arn:aws:s3:::reports-bucket/*" }] } ``` ## How the two combine Within one AWS account, S3 unions the allows. If the caller's identity policy allows `s3:GetObject` but the bucket policy is silent, the call succeeds. If the bucket policy allows it and the identity policy is silent, it also succeeds — this is why an over-broad bucket policy is dangerous even when your IAM policies are tight. Across accounts the rule tightens: the caller's own account must allow the action *and* the bucket's policy must allow that caller. Neither side can grant access unilaterally, which is what makes cross-account sharing safe by construction. On top of that sits the rule that decides ties: an explicit `Deny` in *any* applicable policy — identity policy, bucket policy, a Service Control Policy in the organization, a permissions boundary, a session policy — wins over every `Allow`. Deny is final; absence of an allow is merely an implicit deny that another policy can fill in. ## Resource ARNs: the bucket and the objects are different things A very common junior mistake is writing one `Resource` and expecting it to cover everything. Bucket-level actions such as `s3:ListBucket`, `s3:GetBucketLocation` and `s3:PutBucketPolicy` take the bucket ARN, `arn:aws:s3:::my-bucket`. Object-level actions such as `s3:GetObject`, `s3:PutObject` and `s3:DeleteObject` take the object ARN pattern, `arn:aws:s3:::my-bucket/*`. A policy that lists only `arn:aws:s3:::my-bucket` will allow directory-style listing but every object GET will fail; a policy that lists only `arn:aws:s3:::my-bucket/*` will read objects fine while `aws s3 ls` returns AccessDenied. Most real policies contain two statements, one per ARN shape. ## What only a bucket policy can do Three capabilities exist only on the resource side: 1. **Anonymous access.** `"Principal": "*"` with no condition grants the whole internet. There is no identity policy for an anonymous caller, so this can only be expressed on the bucket. It is the mechanism behind almost every "public S3 bucket" headline, and the reason S3 Block Public Access is enabled by default on new buckets. 2. **Cross-account grants.** Naming another account's principal in `Principal` is how you offer access outward. 3. **Conditions on the request's origin.** Keys such as `aws:SourceVpce`, `aws:SourceIp` and `aws:PrincipalOrgID` let the bucket refuse traffic that does not arrive the way you expect, regardless of what the caller's own account permits. ## Practical guidance Default to identity policies: they scale with your principals, live with your roles, and are easy to audit per team. Reach for a bucket policy when the statement is genuinely about the bucket — sharing outward to another account, forbidding a class of request no matter who makes it, or setting a guardrail you want to survive changes to IAM. Keep bucket policies short: they are limited to 20 KB, and a bucket policy that has grown into a directory of every team in the company is a sign you should be delegating instead.

  • If the identity policy allows s3:PutObject and the bucket policy explicitly denies it, what happens?
    The request is denied. An explicit `Deny` in any applicable policy — bucket policy, identity policy, SCP, permissions boundary or session policy — overrides every `Allow`, and there is no ordering or specificity rule that can rescue it. This is why teams put non-negotiable guardrails in a `Deny` statement rather than relying on the absence of an allow.
  • Why do so many S3 policies need two statements for what feels like one permission?
    Because S3 has two resource types. Bucket-level actions such as `s3:ListBucket` are authorized against `arn:aws:s3:::bucket`, while object-level actions such as `s3:GetObject` are authorized against `arn:aws:s3:::bucket/*`. One statement cannot naturally cover both, so the idiomatic policy has one statement per ARN shape.
  • When would you deliberately choose a bucket policy over an identity policy inside a single account?
    When the rule belongs to the data rather than to a person: a guardrail you want to hold no matter which role is created later, a condition on where requests may originate, or a grant that must be visible to auditors when they look at the bucket. Everything else is cleaner as an identity policy attached to the role.

An identity policy is the badge in your pocket listing the doors you may open; a bucket policy is the guest list taped to one door listing who may come in.

saying these in an interview costs you the question

  • Says a bucket policy needs no Principal element
  • Thinks bucket policies replace IAM policies entirely
  • Uses one ARN and expects ListBucket and GetObject both to work
  • Believes an Allow can override an explicit Deny
  • Assumes an allow in only one account is enough cross-account

context