AWS IAM Deep Dive Cheat Sheet
Reference for IAM users, roles, policies, and permission boundaries used to secure access to AWS resources.
IAM Policy Document
Standard structure of an identity-based policy.
{ "Version": "2012-10-17", "Statement": [ { "Sid": "AllowS3ReadOnly", "Effect": "Allow", "Action": ["s3:GetObject", "s3:ListBucket"], "Resource": [ "arn:aws:s3:::my-bucket", "arn:aws:s3:::my-bucket/*" ], "Condition": { "StringEquals": {"aws:RequestedRegion": "us-east-1"} } } ]}
Role Trust Policy
Allows an EC2 instance to assume a role.
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": {"Service": "ec2.amazonaws.com"}, "Action": "sts:AssumeRole" } ]}
CLI: Users, Roles & Policies
Common IAM management commands.
aws iam create-user --user-name janeaws iam create-role --role-name my-role \ --assume-role-policy-document file://trust-policy.jsonaws iam attach-role-policy --role-name my-role \ --policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccessaws iam list-attached-user-policies --user-name janeaws iam create-access-key --user-name janests assume-role would use: aws sts assume-role --role-arn arn:aws:iam::123456789012:role/my-role --role-session-name session1
Core Concepts
Key core concepts to know.
- User- Long-term identity for a person or application with credentials
- Role- Temporary identity assumed by users, services, or federated identities
- Policy- JSON document defining allowed/denied actions on resources
- Managed vs Inline Policy- Managed policies are reusable/standalone; inline policies are embedded in one identity
- Permission Boundary- Sets the maximum permissions an identity can ever have, regardless of attached policies
- Least Privilege- Grant only the exact permissions required, nothing more
Policy Evaluation Logic
Key policy evaluation logic to know.
- Explicit Deny- Always wins over any Allow, in any policy
- Default Deny- All requests are denied unless explicitly allowed
- SCP (Org level)- Service Control Policies set the max permissions across an AWS Organization
- Resource Policy- Attached to the resource itself (e.g. S3 bucket policy), evaluated alongside identity policies
Conditional AssumeRole with Session Tags
Pass session tags at assume-role time to drive attribute-based access control (ABAC).
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": {"AWS": "arn:aws:iam::123456789012:root"}, "Action": "sts:AssumeRole", "Condition": { "StringEquals": {"sts:ExternalId": "partner-shared-secret"}, "Bool": {"aws:MultiFactorAuthPresent": "true"}, "NumericLessThan": {"aws:MultiFactorAuthAge": "3600"} } }, { "Effect": "Allow", "Principal": {"AWS": "arn:aws:iam::123456789012:root"}, "Action": "sts:TagSession" } ]}
Attribute-Based Access Control (ABAC) Policy
Grant access dynamically by matching a resource tag to the principal's session tag.
{ "Version": "2012-10-17", "Statement": [ { "Sid": "AllowAccessIfOwnerTagMatches", "Effect": "Allow", "Action": ["ec2:StartInstances", "ec2:StopInstances"], "Resource": "arn:aws:ec2:*:*:instance/*", "Condition": { "StringEquals": { "aws:ResourceTag/Owner": "${aws:PrincipalTag/Owner}" } } } ]}
OIDC Federation for CI/CD (GitHub Actions)
Trust policy allowing GitHub Actions to assume a role without long-lived AWS keys.
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com" }, "Action": "sts:AssumeRoleWithWebIdentity", "Condition": { "StringEquals": { "token.actions.githubusercontent.com:aud": "sts.amazonaws.com" }, "StringLike": { "token.actions.githubusercontent.com:sub": "repo:my-org/my-repo:ref:refs/heads/main" } } } ]}
CLI: Simulate & Analyze Policies
Test what a principal can actually do before granting it, and diff policy versions.
# Dry-run whether an identity can perform an action on a resourceaws iam simulate-principal-policy \ --policy-source-arn arn:aws:iam::123456789012:user/jane \ --action-names s3:DeleteObject \ --resource-arns arn:aws:s3:::my-bucket/reports/q1.csv# List an identity's actually-used services from the last 400 daysaws iam generate-service-last-accessed-details \ --arn arn:aws:iam::123456789012:role/my-roleaws iam get-service-last-accessed-details --job-id <job-id># Compare policy versions before rollbackaws iam list-policy-versions --policy-arn arn:aws:iam::123456789012:policy/MyPolicyaws iam get-policy-version --policy-arn arn:aws:iam::123456789012:policy/MyPolicy --version-id v3
Advanced IAM Mechanics
How the policy engine actually resolves access beyond simple allow/deny.
- Policy Evaluation Order- Explicit Deny > Organizations SCP > Resource Policy/Permission Boundary intersection > Identity Policy > implicit Deny
- NotAction / NotResource- Inverts the match set — grants everything except the listed actions/resources; dangerous if combined carelessly with Allow
- Confused Deputy Problem- Cross-service Deny bypass mitigated with `aws:SourceArn` and `aws:SourceAccount` conditions on resource policies
- Session Duration- Max 12h for assumed roles (`--duration-seconds`), 6h default cap for `AssumeRoleWithWebIdentity`
- IAM Identity Center (SSO)- Permission sets are provisioned as IAM roles per account; edits require re-provisioning to propagate
- Credential Report- `aws iam generate-credential-report` / `get-credential-report` audits key age, MFA status, and password rotation account-wide
- Service Control Policy vs Permission Boundary- SCPs cap an entire OU/account at the Organizations layer; permission boundaries cap a single IAM identity
Use IAM Access Analyzer to detect resources shared with external entities and to generate least-privilege policies from actual CloudTrail activity instead of guessing required permissions.