AWS IAM Policy Structure: Decoding Effect, Action, Resource, and Condition
Every AWS permission decision — whether a Lambda function can write to S3, or a developer can delete a DynamoDB table — ultimately traces back to a JSON IAM policy document. Misreading even one of its four core elements can mean the difference between a working system and a silent access denial that takes hours to debug.
TL;DR — The Four Core IAM Policy Elements
| Element | Role in the Policy | Valid Values / Format | Required? |
|---|---|---|---|
Effect |
The verdict — allow or deny this statement | "Allow" or "Deny" |
✅ Yes |
Action |
The operation(s) being governed | Service prefix + action (e.g., s3:GetObject), supports wildcards |
✅ Yes |
Resource |
The specific AWS resource(s) the action applies to | ARN string or "*" for all resources |
✅ Yes |
Condition |
Contextual guard — when the statement applies | Condition operator + key + value map | ❌ Optional |
The Anatomy of an IAM Policy Statement
An IAM policy is a JSON document containing one or more statements. Each statement is a self-contained access rule. Think of the policy document as a rulebook, and each statement as a single rule within it.
Here is a canonical policy with all four elements present:
🔽 [Click to expand] — Full IAM Policy Example
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowS3ReadFromCorpNetwork",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::my-company-data-bucket",
"arn:aws:s3:::my-company-data-bucket/*"
],
"Condition": {
"IpAddress": {
"aws:SourceIp": "203.0.113.0/24"
}
}
}
]
}
Deep-Dive: Each Element Explained
1. Effect — The Verdict
Effect is binary: "Allow" or "Deny". It is the final word of the statement. AWS IAM operates on a default-deny model — unless an explicit Allow exists, access is denied. An explicit Deny always overrides any Allow, regardless of policy source or order.
Real-World Analogy: Think of IAM policy evaluation like a building's security system. By default, every door is locked (implicit deny). A keycard grant (Allow) opens specific doors. But a security lockdown order (explicit Deny) overrides all keycards — even the CEO's — for those specific doors. The lockdown always wins.
2. Action — The Operation
Action specifies which AWS API operation(s) the statement controls. The format is always service-prefix:OperationName. You can specify a single action, a list, or use the * wildcard.
- Single action:
"Action": "s3:GetObject" - Multiple actions:
"Action": ["s3:GetObject", "s3:PutObject"] - Wildcard (use with caution):
"Action": "s3:*"— grants all S3 operations - Full admin (avoid in production):
"Action": "*"
⚠️ Least Privilege Principle: Always enumerate only the specific actions your workload requires. Avoid "Action": "*" in any production policy. Use IAM Access Advisor to identify unused permissions and tighten policies over time.
3. Resource — The Target
Resource scopes the statement to specific AWS resources identified by their ARN (Amazon Resource Name). This is where many engineers make critical mistakes — applying overly broad permissions by defaulting to "Resource": "*".
Key nuance for S3: A bucket and its objects have separate ARNs. To allow both listing a bucket and reading its objects, you must specify both:
"Resource": [
"arn:aws:s3:::my-company-data-bucket",
"arn:aws:s3:::my-company-data-bucket/*"
]
The first ARN covers bucket-level actions (e.g., s3:ListBucket). The second covers object-level actions (e.g., s3:GetObject). Omitting either is a common source of AccessDenied errors.
4. Condition — The Contextual Guard
Condition is optional but powerful. It adds contextual constraints that must be satisfied for the statement to apply. A condition block has the structure: ConditionOperator → ConditionKey → Value.
"Condition": {
"StringEquals": {
"aws:RequestedRegion": "us-east-1"
}
}
Common use cases for conditions:
- IP restriction:
aws:SourceIp— limit access to a corporate IP range - MFA enforcement:
aws:MultiFactorAuthPresent— require MFA for sensitive actions - Region lock:
aws:RequestedRegion— prevent actions outside approved regions - Tag-based access:
aws:ResourceTag/Environment— scope access by resource tags (ABAC) - Secure transport:
aws:SecureTransport— enforce HTTPS-only access
How IAM Evaluates a Policy Statement — Logic Flow
When an API call is made, IAM evaluates all applicable policies. The following diagram shows how the four elements interact during a single statement evaluation:
- API Call Received: A principal (user, role, or service) makes an AWS API request.
- Action Match: IAM checks if the requested operation matches the
Actionelement. If not, this statement is skipped. - Resource Match: IAM checks if the target resource ARN matches the
Resourceelement. If not, this statement is skipped. - Condition Evaluation: If a
Conditionblock exists, all conditions must evaluate to true. A single false condition causes the entire statement to be skipped. - Effect Applied: If all checks pass, the
Effect(AlloworDeny) is applied to the authorization decision.
Complete Policy Evaluation Order (Multi-Policy Context)
Understanding individual statement elements is step one. In real environments, multiple policies apply simultaneously. Here is the AWS policy evaluation order:
- Explicit Deny check: If any policy contains an explicit
Denymatching the request, access is immediately denied. No further evaluation occurs. - Organizations SCP check: If the account is in an AWS Organization, Service Control Policies are evaluated. The request must be allowed by the SCP.
- Resource-based policy check: If the target resource has a resource-based policy (e.g., S3 bucket policy), it is evaluated.
- Identity-based policy check: The principal's attached IAM policies (inline + managed) are evaluated for an explicit Allow.
- Default Deny: If no explicit Allow is found across all evaluated policies, access is denied.
Practical Example: MFA-Protected S3 Delete
This policy allows deleting objects from a specific S3 bucket only when the request is authenticated with MFA — a common pattern for protecting critical data:
🔽 [Click to expand] — MFA-Protected Delete Policy
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyDeleteWithoutMFA",
"Effect": "Deny",
"Action": "s3:DeleteObject",
"Resource": "arn:aws:s3:::my-company-data-bucket/*",
"Condition": {
"BoolIfExists": {
"aws:MultiFactorAuthPresent": "false"
}
}
},
{
"Sid": "AllowDeleteWithMFA",
"Effect": "Allow",
"Action": "s3:DeleteObject",
"Resource": "arn:aws:s3:::my-company-data-bucket/*",
"Condition": {
"Bool": {
"aws:MultiFactorAuthPresent": "true"
}
}
}
]
}
Glossary
| Term | Definition |
|---|---|
| Principal | The IAM entity (user, role, service) making the API request. |
| ARN | Amazon Resource Name — a globally unique identifier for any AWS resource (e.g., arn:aws:s3:::bucket-name). |
| Explicit Deny | A statement with "Effect": "Deny" that unconditionally overrides any Allow in any policy. |
| Condition Operator | The comparison method in a Condition block (e.g., StringEquals, IpAddress, Bool). |
| ABAC | Attribute-Based Access Control — using resource/principal tags in Condition elements to dynamically scope permissions. |
Next Steps
Mastering these four elements is the foundation of all AWS security work. From here, explore these official resources to deepen your IAM expertise:
- 📖 IAM JSON Policy Elements Reference — AWS Docs
- 🔍 Policy Evaluation Logic — AWS Docs
- 🛠️ IAM Policy Simulator — test policies before deploying them
- 📋 AWS Global Condition Context Keys — full list of available condition keys
Comments
Post a Comment