---
cuppy_hub: aws
cuppy_path: reference/iam/policy-grammar-and-conditions.md
cuppy_section: docs
cuppy_doc: NV25-312X
cuppy_doc_uuid: b72ddcb8-2800-4551-9a5d-81c46f9e68d5
cuppy_permalink: https://cuppy.io/aws/d/NV25-312X
cuppy_version: 4
cuppy_updated: 2026-10-23T16:16:52.575Z
cuppy_canonical: https://cuppy.io/aws/docs/reference/iam/policy-grammar-and-conditions
cuppy_mcp: https://cuppy.io/mcp
cuppy_body_sha256: 5d82b740382ef59d313df054deaed6cbceee8106074381889a2b40cef45ef706
# cuppy_body_sha256 is over the document body ALONE, excluding both generated blocks.
---


# Policy grammar and conditions

**Never guess an action, resource type or condition key name.** Look it up for the exact service (last section), write the narrowest statement, and remember the rule that catches most agents: **a condition on a key the request does not carry evaluates to false**, so an Allow silently stops applying, except with negated operators and `IfExists`, which flip that.

## Elements

A policy has a `Version` and one or more `Statement` entries. Each statement has an `Effect` (`Allow` or `Deny`), what it applies to, and optionally a `Sid` and a `Condition`. Element order does not matter.

| Element | Notes |
|---|---|
| `Principal` / `NotPrincipal` | Only in **resource-based** policies (including trust policies); an identity-based policy cannot contain `Principal`. |
| `Action` / `NotAction` | `service-prefix:ActionName`; `*` and `?` wildcards allowed. |
| `Resource` / `NotResource` | ARNs; the ARN format for each resource type is in the service reference. |
| `Condition` | Optional; see below. |

Each pair is **mutually exclusive in one statement**: `Action` or `NotAction`, `Principal` or `NotPrincipal`, `Resource` or `NotResource`. Use `"Version": "2012-10-17"`.

## How a Condition block combines

```json
"Condition": {
  "StringEquals": { "aws:PrincipalOrgID": "o-example", "aws:SourceVpce": ["vpce-1111aaaa", "vpce-2222bbbb"] },
  "Bool": { "aws:SecureTransport": "true" }
}
```

- **Several values under one key: OR.** Either endpoint above matches.
- **Several keys, or several operators: AND.** Every key under every operator must match.
- In a **Deny**, the same AND applies: the Deny only fires when all of its conditions match.
- Condition key **names** are case-insensitive. Two request keys differing only in case can make a condition behave unexpectedly.

## Operators and their traps

| Family | Operators | Notes |
|---|---|---|
| String | `StringEquals`, `StringNotEquals`, `StringEqualsIgnoreCase`, `StringNotEqualsIgnoreCase`, `StringLike`, `StringNotLike` | `Equals` and `Like` are case-sensitive; `Like` supports `*` and `?`. |
| ARN | `ArnEquals`, `ArnLike`, `ArnNotEquals`, `ArnNotLike` | `ArnEquals` and `ArnLike` behave the same; each of the six ARN fields is matched separately and may contain wildcards. Use for `aws:SourceArn`, `aws:PrincipalArn`. |
| Numeric | `NumericEquals`, `…NotEquals`, `…LessThan(Equals)`, `…GreaterThan(Equals)` | No wildcards, no policy variables. |
| Date | `DateEquals`, `…NotEquals`, `…LessThan(Equals)`, `…GreaterThan(Equals)` | ISO 8601 or epoch; with `aws:CurrentTime`, `aws:EpochTime`, `aws:TokenIssueTime`. |
| Bool | `Bool` | `true` / `false`. |
| IP | `IpAddress`, `NotIpAddress` | CIDR; a bare address means `/32`. `aws:SourceIp` is **absent** through a VPC endpoint, and is the calling service's IP when a service calls on your behalf. |
| Binary | `BinaryEquals` | Base64 value. |
| Presence | `Null` | `"true"` = key absent, `"false"` = key present. |

**Missing keys.** A plain operator on an absent key is false. A negated operator (`StringNotEquals`, `ArnNotLike`, …) on an absent key is **true**. That is why a Deny with a negated operator also denies requests that lack the key.

**`IfExists`** (on any operator except `Null`): if the key is present, test it; if absent, the condition is true. Use it when one statement covers several resource types and only some carry the key. With `Deny` and a negated `…IfExists` operator, a missing key still leads to a deny.

**MFA.** `aws:MultiFactorAuthPresent` is absent for long-term access keys and federated identities. A Deny using `"BoolIfExists": {"aws:MultiFactorAuthPresent": "false"}` therefore also blocks scripts using access keys. A plain `Bool` with `false` is not a reliable MFA check.

## Multivalued keys: `ForAllValues` and `ForAnyValue`

For keys that carry a set (such as `aws:TagKeys`), prefix a string, ARN or Bool operator:

| Qualifier | True when |
|---|---|
| `ForAnyValue:` | at least one request value matches |
| `ForAllValues:` | every request value matches one of the policy values |
| `ForAllValues:…Not…` | no request value matches |
| `ForAnyValue:…Not…` | at least one request value does not match |

**Trap:** `ForAllValues` is **true when the key is missing or empty**, so an Allow restricted with it can be satisfied by sending nothing. Pair it with a `Null` check that requires the key:

```json
"Condition": {
  "ForAllValues:StringEquals": { "aws:TagKeys": ["environment", "team"] },
  "Null": { "aws:TagKeys": "false" }
}
```

## Policy variables

`${aws:username}`, `${aws:PrincipalTag/<key>}` and similar work with string, ARN and Bool operators. They do not work with numeric, date, IP, binary or `Null` operators.

## Global keys agents use most

| Key | Holds | Note |
|---|---|---|
| `aws:PrincipalArn` | ARN of the caller; for a role session, the **role** ARN, not the session ARN | Survives role deletion and recreation, unlike a `Principal` ARN |
| `aws:PrincipalOrgID` | Organization ID of the caller | Org-wide grants without listing accounts |
| `aws:SourceArn` / `aws:SourceAccount` | The resource or account a **service** is acting for | Confused-deputy protection on resource policies; `aws:SourceAccount` exists only for direct service-principal calls |
| `aws:SourceOrgID` / `aws:SourceOrgPaths` | The organization or OU path a service is acting for | Same, at organization scope |
| `aws:SourceVpce` | VPC endpoint ID | Present only on the endpoint path |
| `aws:SourceIp` | Caller IP | Absent through VPC endpoints |
| `aws:SecureTransport` | Whether TLS was used | Deny `"false"` to require TLS |
| `aws:MultiFactorAuthPresent` | MFA on temporary credentials | Absent for long-term keys; use `BoolIfExists` |
| `aws:TokenIssueTime` | When temporary credentials were issued | Absent for long-term keys; `"Null": {"aws:TokenIssueTime": "false"}` requires temporary credentials |
| `aws:userid` | Caller's unique ID | Allow-lists keyed on it are a common explicit-deny cause |

## Looking up exact names

- **Service Authorization Reference**: the page per service lists every action, the resource types it authorizes, and the service-specific condition keys.
- **Machine-readable service reference**: `https://servicereference.us-east-1.amazonaws.com/` lists services with a JSON URL each (for example `…/v1/s3/s3.json`), giving actions with their condition keys and resource types, condition key types, resource ARN formats, and operation-to-action mappings with SDK method names. `…/v1/mapping.json` maps SDK service names to entries. Updates can take up to 24 hours to appear. Each action carries `IsList`, `IsWrite`, `IsPermissionManagement` and `IsTaggingOnly` flags; filtering on `IsPermissionManagement` finds the actions that can change permissions.

An operation may need more than one action (S3 `GetObject` can authorize several); the operations section of the service reference lists them where the service supports that mapping.


---
<!-- cuppy -->
You are reading one file. The same content is free over MCP at https://cuppy.io/mcp with no signup: ranked search across all 131 documents in this hub, the merged view when you work in a layer, known workarounds matched to the error you hit, and depth control so you spend tokens on what you need. Start with the `_hello` tool. How to connect: https://cuppy.io/aws/ Something wrong in this document? Report it with the `session feedback` tool on the same endpoint.
<!-- /cuppy -->
