What is an AWS IAM Policy?
An AWS IAM Policy is a JSON document that defines permissions in AWS. Learn the policy types, evaluation order, and how to write least-privilege policies.
What is an AWS IAM Policy?
Definition
An AWS IAM Policy is a JSON document that defines permissions in AWS. Policies state what actions are allowed or denied on which resources, optionally under specific conditions.
Policies are attached to identities (users, groups, roles), resources, or scopes (organization-level SCPs), and AWS evaluates them on every API call to authorize the request.
In simple terms:
A policy is a JSON contract: under conditions X, principal Y can/cannot do action Z on resource W.
Why Policies Matter
- They are the language of AWS authorization.
- Almost every AWS API call is authorized by policy evaluation.
- Misconfigured policies are the #1 cause of AWS data exposure.
- Policies must be carefully written to avoid over-permissioning, privilege escalation, and unintended public exposure.
Anatomy of an IAM Policy
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowS3ReadWithMFA",
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:ListBucket"],
"Resource": [
"arn:aws:s3:::my-bucket",
"arn:aws:s3:::my-bucket/*"
],
"Condition": {
"Bool": { "aws:MultiFactorAuthPresent": "true" },
"IpAddress": { "aws:SourceIp": "203.0.113.0/24" }
}
}
]
}
Key Elements
- Version — always
2012-10-17for current policy language. - Statement — array of statements.
- Sid — optional statement ID.
- Effect —
AlloworDeny. - Action — API operations (e.g.,
s3:GetObject). - NotAction — opposite of Action; matches everything except.
- Resource — ARNs the statement applies to.
- NotResource — opposite of Resource.
- Principal — used in resource-based and trust policies (who).
- Condition — context-based restrictions (MFA, IP, time, tags, etc.).
Policy Types
1. Identity-Based Policies
Attached to IAM identities (users, groups, roles).
- AWS managed — created/maintained by AWS.
- Customer managed — your reusable policies.
- Inline — embedded in a specific identity.
2. Resource-Based Policies
Attached to AWS resources (S3 buckets, KMS keys, SNS topics, Lambda, etc.). Define who from where can do what to the resource.
- Always include
Principal. - Can grant cross-account access without identity-based policy in source account.
3. Permission Boundaries
A policy attached to a user or role that caps the maximum permissions the principal can have. Even if identity-based policy says Allow, boundary must also allow.
Used to safely delegate IAM administration.
4. Service Control Policies (SCPs)
At the AWS Organizations level. Apply to entire accounts/OUs. SCPs only deny or allow (limit) — they never grant new permissions.
5. Session Policies
Passed during AssumeRole / GetFederationToken to further restrict the resulting session.
6. Resource Control Policies (RCPs)
Newer addition — limit permissions on resources at Organization level (similar to SCPs but resource-side).
Policy Evaluation Logic
For each request, AWS evaluates all applicable policies:
- Explicit Deny anywhere → Deny.
- Service Control Policies / Resource Control Policies must permit (or be absent).
- Permission Boundary must permit (if attached).
- Session policy must permit (if assumed role).
- Identity-based or resource-based Allow required.
- Implicit Deny if no Allow.
A request is allowed only if all relevant scopes allow and none denies.
Common Patterns
Read-Only Auditor
{ "Version": "2012-10-17",
"Statement": [{
"Effect": "Allow", "Action": ["*:Describe*","*:Get*","*:List*"], "Resource": "*"
}]
}
S3 Bucket Restricted to Single Role
{ "Version": "2012-10-17", "Statement": [
{ "Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::111111111111:role/MyAppRole" },
"Action": ["s3:GetObject"],
"Resource": "arn:aws:s3:::my-data/*" }
]}
Deny Without MFA (SCP/Identity)
{ "Version": "2012-10-17",
"Statement": [{
"Effect": "Deny",
"Action": "*",
"Resource": "*",
"Condition": { "BoolIfExists": { "aws:MultiFactorAuthPresent": "false" } }
}]
}
Tag-Based Access
{ "Effect": "Allow",
"Action": ["ec2:StartInstances", "ec2:StopInstances"],
"Resource": "*",
"Condition": {
"StringEquals": { "ec2:ResourceTag/Owner": "${aws:username}" }
}}
Common Pitfalls
Action: "*"andResource: "*"— admin-equivalent.- Wildcards in IAM actions (
iam:*) — combined withiam:PassRole, classic escalation. - No condition on sensitive permissions.
- Inline policies sprinkled across identities — hard to audit.
- Resource-based policies granting cross-account
Principal: "*"— accidental public access. - Forgotten public S3 bucket policies.
- Policies allowing
iam:CreatePolicyVersion— privilege escalation by replacing policy content. - Trust policies trusting
Principal: "*"— anyone can assume. - Conditions written incorrectly (e.g.,
aws:MultiFactorAuthPresent: falseinstead oftrue).
Real-World Examples
1. Public S3 Bucket via Bucket Policy
Resource-based policy with Principal: "*" and s3:GetObject exposed sensitive data publicly. Common pattern in many breaches.
2. iam:PassRole Privilege Escalation
User has iam:PassRole on a high-privilege role and lambda:CreateFunction. They create a Lambda with the high-privilege role and execute it — escalating to admin.
3. AdministratorAccess Attached "Just for Now"
Developer attaches AdministratorAccess to debug. Never removes. Becomes attack surface.
4. Cross-Account Confused Deputy
Trust policy lets arn:aws:iam::partner-account:root assume the role with no external ID. A different customer of partner could assume your role.
Best Practices
- Least privilege — explicit actions and resource ARNs.
- Use customer-managed policies with version control (Git).
- Avoid
*:*policies in production. - Review with IAM Access Analyzer — public/cross-account / unused permission detection.
- Use permission boundaries for delegated admin.
- Use SCPs for organization-wide guardrails (deny outside region, deny root usage, deny MFA bypass).
- MFA conditions on sensitive actions.
- External ID in cross-account trust policies.
- Tag-based access control where appropriate.
- Monitor IAM events in CloudTrail with detections.
- Periodic dead-policy cleanup.
Checklist
- Policies in version control with peer review?
- No
*:*policies in production? - IAM Access Analyzer enabled and findings actioned?
- Permission boundaries on delegated admins?
- SCPs enforce baseline guardrails?
- MFA / IP conditions on sensitive actions?
- External IDs for third-party trust?
- Quarterly cleanup of unused policies and stale ones?
- CloudTrail monitoring on policy changes?
How Forestall Helps
Forestall analyzes every policy across every account:
- Effective permissions per principal.
- Privilege escalation paths via dangerous IAM patterns.
- Public/cross-account exposure via resource policies.
- Stale and unused permissions.
- Policy-change blast radius prediction.
Frequently Asked Questions
What's the difference between Allow and Deny?
Allow grants permission; Deny overrides any Allow. AWS resolves in favor of Deny.
Can I have multiple statements in one policy?
Yes — each policy can contain many statements; each evaluated independently.
What's the size limit?
Managed policies up to 6,144 characters; inline up to similar; users have aggregate limits. Policy size is a real constraint at scale.
What's iam:PassRole?
Permission to pass a role to an AWS service (so it can assume it). Powerful — must be scoped to specific roles.
How do conditions work?
Condition operators (StringEquals, IpAddress, Bool, etc.) check context keys (aws:SourceIp, aws:MultiFactorAuthPresent, etc.) at request time.
Conclusion
IAM policies are the language of AWS access control. Used carelessly, they create the over-permissioning and escalation paths that drive most AWS breaches. Used carefully — with least privilege, conditions, boundaries, SCPs, version control, peer review, and continuous analysis — they become the rigorous, auditable control surface AWS intends them to be.
Find your overly permissive IAM policies.
Forestall analyzes IAM policies for over-permissioning, escalation paths, and dangerous patterns.