Walk through the elements of an AWS IAM policy statement — Effect, Action, Resource, Condition and Principal — and explain why Principal appears in some IAM policies but not others.
answer
- JSON with a version and statements
- one mandatory element is a two-value enum
- who is being granted access
- identity policies already know the subject
- bucket ARN and object ARN differ
basics
~20 sAn IAM policy statement combines Effect (Allow or Deny), Action, Resource ARNs and an optional Condition. Principal appears only in resource-based policies, because an identity-based policy is already attached to the principal it applies to.
solid answer
~50 sAn IAM policy is a JSON document with a language `Version` (`2012-10-17`) and one or more statements. Each statement has an optional `Sid`, a mandatory `Effect` of exactly `Allow` or `Deny`, an `Action` list of service-qualified API names such as `s3:GetObject`, a `Resource` list of ARNs the actions apply to, and an optional `Condition` block that must evaluate true for the statement to match. `Principal` names *who* is being granted access — it belongs in resource-based policies such as an S3 bucket policy or an SQS queue policy, where the document lives on the resource and has to say which account, role or service principal it trusts. An identity-based policy attached to a user or role omits `Principal` entirely, because the attachment already answers that question. The mirror elements `NotAction`, `NotResource` and `NotPrincipal` exist but are easy to get wrong and are rarely the right tool.
code
json · 18 lines{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ListTheBucket",
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::reports"
},
{
"Sid": "ReadObjectsUnderPrefix",
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:GetObjectVersion"],
"Resource": "arn:aws:s3:::reports/monthly/*",
"Condition": { "Bool": { "aws:SecureTransport": "true" } }
}
]
}go deeper
Be ready to name the elements and say plainly that Effect is only Allow or Deny, actions are written service:Operation, and Resource takes full ARNs.
Explain why Principal is meaningful only on a resource-based policy, and show the bucket-versus-object ARN distinction on a concrete S3 example.
Show how you review a handed policy for over-grant: wildcard actions, unscoped resource ARNs, missing conditions, and NotAction statements that grant future services.
Own the standard the organisation writes policies to — what a reviewable policy looks like, where wildcards are tolerated, and how that standard is enforced rather than requested.
## The shape of a policy document Every AWS permission is expressed as a JSON policy document. The top level has two keys: ```json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::reports/*" } ] } ``` `Version` is the **policy language version**, not a date you can bump. `2012-10-17` is the current one and is required for features such as policy variables; the older `2008-10-17` silently disables them. `Statement` is a single object or an array of them, and each statement is evaluated independently. ## The elements, one by one **Sid** — an optional statement identifier. It is a label for humans and for tooling; in an identity-based policy it has no functional effect, and it does not need to be unique across policies. **Effect** — mandatory, and exactly one of the two strings `Allow` or `Deny`. There is no default and no third value; a missing or misspelled `Effect` makes the document invalid rather than permissive. **Action** — one string or an array of service-qualified API operation names, always `service-prefix:OperationName`, for example `s3:GetObject`, `ec2:RunInstances`, `sts:AssumeRole`. The prefix is the service's IAM namespace, which is not always the marketing name. Wildcards are allowed inside the name (`s3:Get*`) and as a whole (`"Action": "*"`). Action names are case-insensitive on evaluation but conventionally written in the service's own casing. Not every API call maps one-to-one to an action, and some actions are permission-only — they exist to gate behaviour and have no matching API call. **Resource** — the ARNs the actions apply to, in the form `arn:partition:service:region:account-id:resource`. Global or account-scoped resources leave fields empty: an S3 bucket ARN has no region and no account (`arn:aws:s3:::reports`), and IAM ARNs have no region. The single most common beginner error lives here: **the bucket and the objects inside it are different resources**. `s3:ListBucket` acts on `arn:aws:s3:::reports`, while `s3:GetObject` acts on `arn:aws:s3:::reports/*`. Grant only one of the two ARNs and half the workflow fails with AccessDenied. Some actions do not operate on a resource at all and must be written with `"Resource": "*"`. **Condition** — an optional block of operators and context keys that all have to evaluate true for the statement to apply. `{"StringEquals": {"aws:RequestedRegion": "eu-west-1"}}` is a typical shape. Conditions are how a policy stops being a blunt grant and starts encoding real intent. **Principal** — present only in **resource-based** policies: bucket policies, queue policies, topic policies, Lambda function policies, and the trust policy on a role. It names the identity the resource extends access to, in one of several shapes: `{"AWS": "arn:aws:iam::123456789012:role/app"}`, `{"Service": "lambda.amazonaws.com"}`, `{"Federated": "..."}`, or the wildcard `"*"`. Note that `arn:aws:iam::123456789012:root` in a `Principal` does **not** mean the account's root user — it delegates to the whole account, which then decides internally which of its principals may use the grant. An identity-based policy has no `Principal` element at all; adding one makes the document invalid. ## The negated forms `NotAction`, `NotResource` and `NotPrincipal` match everything *except* what they list. They read like a clever shortcut and behave like a footgun: `{"Effect": "Allow", "NotAction": "iam:*", "Resource": "*"}` grants every action in every service AWS has ever launched, plus every one it launches tomorrow, minus IAM. Use them only when you genuinely mean "everything but", and prefer a `Deny` statement when you mean "except this". ## Reading a policy fluently When you are handed a policy in an interview, read it in this order: what is the effect, which service namespaces appear in the actions, how tightly are the resource ARNs scoped, and is there a condition narrowing it. `"Action": "*"` with `"Resource": "*"` and no condition is administrator access, whatever the policy is named. A policy with tight ARNs and a condition on a request context key is the shape least-privilege actually takes.
- Where would you put the Principal element in an identity-based policy attached to a role?Nowhere — an identity-based policy has no `Principal` element, and IAM rejects the document if you add one. The attachment to the role already identifies the principal. If you need to name who is trusted, you are writing a resource-based policy instead, such as a bucket policy or a role's trust policy.
- What does an empty region or account field in an ARN mean?It means the resource is not scoped by that dimension. S3 bucket ARNs carry neither region nor account because bucket names are globally unique, and IAM ARNs have no region because IAM is a global service. It is not a wildcard — you cannot leave a field blank for a service that requires it.
- Why is NotAction risky in an Allow statement?`NotAction` matches every action except the ones listed, including actions for services that do not exist yet. An Allow with `NotAction` therefore grows silently as AWS launches new APIs. If the intent is "everything except X", express it as a broad Allow plus an explicit Deny on X, which does not drift.
saying these in an interview costs you the question
- Says every IAM policy needs a Principal element
- Uses the bucket/* ARN for s3:ListBucket
- Thinks Effect defaults to Allow when omitted
- Reads iam::123456789012:root as the account root user
- Treats Version as a date you can update