IAM — Cheat Sheet (DVA-C02)
Verified against AWS documentation on 2026-10-12 · ⚠️ = changed vs older material: learn the value shown here (older practice material may say otherwise; full list in notes/iam-guide.md#changed-vs-older-material) · explanations and examples: notes/iam-guide.md.
Root user and account protection
| Topic | Current facts | Memory hook |
|---|---|---|
| Root user | Email + password identity with full access; identity-based policies can't attach to it; only an SCP can restrict a member account's root | Break glass, don't carry |
| Root MFA | ⚠️ Required for every account type (standalone, management, member); register within 35 days of the first console sign-in attempt | No MFA, no root |
| Root MFA devices | Up to 8 of any type; if all are lost, recovery uses the account email and phone number | |
| Root access keys | Never create them; for occasional root CLI work use aws login with the root credentials |
|
| Root password rules | 8–128 characters, ≥ 3 of 4 character types, not the account name or email; the IAM password policy doesn't apply | |
| Root-only tasks, standalone accounts | ⚠️ Change account email, root password or root access keys; close or reopen the account (name, contacts, currency, Regions no longer need root) | |
| Root-only tasks, any account | Restore a locked-out IAM admin; activate IAM access to Billing; S3 MFA Delete; delete an S3 or SQS policy that denies everyone; RI Marketplace seller; GovCloud sign-up | |
| Centralized root access (Organizations) | ⚠️ Since Nov 15, 2024: delete member root password, keys, certificates and MFA; new member accounts then start without root credentials | Root on demand |
sts:AssumeRoot |
From the management or delegated IAM admin account (not with root credentials); Regional STS endpoint only; DurationSeconds ≤ 900 (default 900); scope with sts:TaskPolicyArn |
15 minutes of root, one task |
AssumeRoot task policies |
IAMAuditRootUserCredentials, IAMCreateRootUserPassword, IAMDeleteRootUserCredentials, S3UnlockBucketPolicy, SQSUnlockQueuePolicy |
|
| Detecting root use | CloudTrail root sign-in events → EventBridge + SNS or GuardDuty; AWS Config root-account-mfa-enabled and iam-root-access-key-check |
IAM users and user groups
| Topic | Current facts | Memory hook |
|---|---|---|
| IAM user | Long-term identity: console password, ≤ 2 access keys, signing certificates, SSH public keys, service-specific credentials | |
| When to create IAM users | ⚠️ Only where federation can't work: workloads that can't use roles, third-party tools, CodeCommit, Keyspaces, break-glass access; people federate | Users are the exception |
| User group | Attaches policies to many users; no credentials, can't sign in, can't be a Principal, can't be nested, no built-in all-users group |
Groups are folders, not people |
| A user's identity permissions | Union of the user's own policies and all its groups' policies; any explicit Deny wins |
|
| Same-account vs cross-account access | Same account: an identity policy OR a resource policy can allow; cross-account: both sides must allow | |
| IAM name rules | Unique per account and case-insensitive; user name 64 chars, group name 128, path 512 | |
| IAM ARNs | Region field empty (IAM is global): arn:aws:iam::<account>:user/<path><name>; paths only via API, CLI or CloudFormation |
IAM is global |
| Deleted and recreated user | New unique ID (AIDA…): trust policies naming the old user stop matching; name-based identity policies match the newcomer |
Same name, new person |
DeleteUser with CLI/API |
First remove password, keys, certificates, SSH keys, service-specific credentials, MFA, inline and managed policies, groups; else DeleteConflict (409) |
Empty the drawers first |
| Suspending an IAM user | Attach a deny-all policy, optionally time-bound with aws:CurrentTime |
IAM roles for AWS services
| Topic | Current facts | Memory hook |
|---|---|---|
| IAM role | Identity without long-term credentials; assuming it returns temporary credentials for a session | Borrowed badge |
| Role trust policy | Resource-based policy on the role: Principal + sts:AssumeRole define who may assume it |
Trust = who |
| Role permissions policy | Identity-based policy on the role: defines what the session may do | Permissions = what |
| Service principal format | <service>.amazonaws.com; several services in one Service array; "Service": "*" not allowed |
|
| Service role | Assumed by a service on your behalf; you create and edit it (Lambda execution role, CloudFormation and CodeBuild service roles) | |
| Service-linked role | Path /aws-service-role/, name AWSServiceRoleFor…; trust and permissions owned by the service and read-only to you; no permissions boundary |
Look, don't touch |
| Creating and deleting service-linked roles | Usually automatic, else create-service-linked-role (iam:CreateServiceLinkedRole); delete after dependent resources; may exceed the role quota |
|
| Role session limits | Max session 1–12 h (default 1 h); requests from 900 s; role chaining ≤ 1 h; sessions AWS services assume aren't limited by it | |
| "Role cannot be assumed by Lambda" | Fix the trust policy (lambda.amazonaws.com + sts:AssumeRole), not the permissions |
|
RoleLastUsed |
Returned by get-role: last-used date and Region over the trailing 400 days |
Policy document structure
| Element | Current facts | Memory hook |
|---|---|---|
Version |
"2012-10-17" is current; omitted → 2008-10-17, where policy variables become literal text |
Always 2012 |
Statement |
One per policy (array for several); each statement holds one Effect, one Action/NotAction, one Resource/NotResource |
|
Sid and Id |
Sid labels a statement (letters and digits only in IAM); Id labels a policy and is allowed only in resource-based policies, never in identity-based ones |
Id = resource policies only |
Effect and default deny |
Allow or Deny; no matching allow = implicit deny; explicit Deny beats every Allow |
Deny wins, silence denies |
Action |
prefix:ActionName, case-insensitive, * and ? wildcards, arrays allowed |
|
Resource |
ARN arn:partition:service:region:account:resource; an incomplete ARN in an identity policy is completed with wildcards |
|
| Actions without resource-level permissions | Many Describe*/List*, s3:ListAllMyBuckets, dynamodb:ListTables need "Resource": "*"; a specific ARN grants nothing |
Describe needs a star |
| S3 ARN shapes | s3:ListBucket on arn:aws:s3:::bucket; s3:GetObject/PutObject on arn:aws:s3:::bucket/* |
Bucket vs bucket/* |
Principal |
Only in resource-based and trust policies; account, user, role, service, federated or *; never a group; in an identity policy → MalformedPolicyDocument |
|
| Account as principal | "AWS": "111122223333" = arn:aws:iam::111122223333:root = the whole account, not only its root user |
|
NotPrincipal |
Deny only; not in identity or trust policies, SCPs or RCPs; AWS prefers Deny + ArnNotEquals on aws:PrincipalArn |
|
NotAction |
With Allow: every unlisted action, including future services; with Deny: "deny everything except" guardrail |
NotAction + Allow = blank cheque |
| Managed policy versions | ≤ 5 per policy, one default; roll back with set-default-policy-version; CLI/API 6th version → LimitExceeded (409); inline policies have none |
|
| Freezing a managed policy | Deny both iam:CreatePolicyVersion and iam:SetDefaultPolicyVersion |
Policy conditions and condition keys
| Topic | Current facts | Memory hook |
|---|---|---|
| Condition combination logic | Operators AND, keys AND, values of one key OR, values under a negated operator NOR | |
| Missing condition key | Most operators → false; negated operators → true; ...IfExists → true; Null tests presence |
|
aws:MultiFactorAuthPresent |
Absent for long-term access-key calls → a "deny without MFA" needs BoolIfExists = false |
IfExists catches keys |
aws:MultiFactorAuthAge |
Seconds since MFA sign-in; present only with temporary credentials | |
aws:SourceIp |
Caller's public IP; absent through VPC endpoints (use aws:VpcSourceIp, aws:SourceVpce); calls a service makes for you show the service's address |
|
aws:RequestedRegion |
IAM calls always resolve to us-east-1 → Region-lock policies must exempt global services with NotAction |
IAM lives in us-east-1 |
aws:SecureTransport |
Always present; Deny when false blocks plain-HTTP requests |
|
| ABAC tag keys | aws:PrincipalTag/, aws:ResourceTag/, aws:RequestTag/, aws:TagKeys; in .../<key> forms the key is case-insensitive, the value case-sensitive |
|
ForAllValues: qualifier |
True when the key is missing or empty → add Null = false |
|
| Wildcards in conditions | Only StringLike/StringNotLike (and ARN operators) expand * and ?; StringEquals treats them literally |
|
| One statement, many resource types | ec2:RunInstances + ec2:InstanceType → use StringLikeIfExists |
Testing and troubleshooting policies
| Topic | Current facts | Memory hook |
|---|---|---|
accessanalyzer validate-policy |
100+ grammar and best-practice checks; ERROR, SECURITY_WARNING, WARNING, SUGGESTION; policy types identity, resource, SCP, RCP |
Lint |
| Validation in the console | JSON editor shows findings for managed and user/role inline policies (not group inline); needs access-analyzer:ValidatePolicy |
|
| Custom policy checks | check-no-new-access, check-access-not-granted, check-no-public-access; PASS or FAIL (= might allow); charged per check |
CI gate |
| Clean validation ≠ least privilege | Validation judges syntax and patterns; use custom checks or policy generation to judge access | |
| Implicit vs explicit deny (details: cli-sdk-policies guide) | "no <type> policy allows" → add an allow there; "explicit deny in a <type> policy" → change that deny |
Edit the deny |
| IAM eventual consistency | Writes go to one control plane (us-east-1), then to read-only data planes per Region: create IAM resources in an earlier step, wait, retry with backoff | |
aws iam wait role-exists |
Waits until GetRole sees the role; other endpoints may still lag |
|
| "This policy does not grant any permissions" | Console summary: an action paired with a resource or condition it doesn't support; the policy was still saved |
Password policy
| Topic | Current facts | Memory hook |
|---|---|---|
| Default password policy | No custom policy set: 8–128 characters, ≥ 3 of 4 character types, not the account name or email, never expires | |
| Custom password options | Min length 6–128; expiry 1–1,095 days; reuse prevention 1–24 passwords; character-type requirements | |
| Timing of password policy changes | Length and character rules at the next change; expiry applies immediately to older passwords | |
| Hard expiry | Expired users need an admin reset (iam:UpdateLoginProfile) or a programmatic iam:ChangePassword with active keys |
|
| "Allow users to change their own password" | Account-level grant of iam:ChangePassword (own user) + iam:GetAccountPasswordPolicy; attaches no policy |
|
| Expired console password | Blocks console sign-in only; access keys keep working | |
update-account-password-policy |
No partial updates: omitted options revert to their parameter defaults (length 6, no expiry, no character rules), not to the default policy | Send the whole policy |
| Password policy scope | IAM users only: not root, not access keys; IAM has no lockout setting |
MFA devices and enforcement
| Topic | Current facts | Memory hook |
|---|---|---|
| MFA devices per user | ⚠️ Up to 8 per IAM user and per root user, any mix; one is enough to sign in | Eight is enough |
| MFA types | ⚠️ Passkeys and FIDO security keys (phishing-resistant, recommended), virtual TOTP apps, hardware TOTP tokens | |
| Hardware TOTP vendors | ⚠️ Thales OTP token or display card bought via AWS's links; GovCloud: Hypersecu | |
| SMS MFA | ⚠️ Can no longer be enabled | |
| Passkeys with the CLI | Registered in the console only and not passable to STS → use aws login |
Passkey to CLI? aws login |
| Sharing a device across identities | One FIDO security key can be registered for many identities; each TOTP token or app entry belongs to exactly one user | |
| Virtual MFA serial number | The device ARN arn:aws:iam::<account>:mfa/<name>; hardware tokens use the printed serial |
|
enable-mfa-device |
Two consecutive codes, submitted promptly; out of sync → resync-mfa-device |
|
| Adding or removing another MFA device | Requires authenticating with an existing device | |
iam:DeleteVirtualMFADevice explicit-deny error |
Leftover unassigned virtual device from a cancelled setup → admin deletes it via CLI or API | |
| Enforcing MFA in a policy | Deny + NotAction (MFA self-service, sts:GetSessionToken) + BoolIfExists aws:MultiFactorAuthPresent = false |
Access keys and programmatic access
| Topic | Current facts | Memory hook |
|---|---|---|
| Access key | Access key ID (AKIA…) + secret access key shown only at creation; long-term until deleted |
|
| Access keys per user | 2, not adjustable (the second slot is for rotation) | |
| Temporary credentials | ASIA… key ID + secret + session token, with an expiry |
ASIA = session |
| Preferred programmatic access | ⚠️ aws login, IAM Identity Center (aws configure sso), CloudShell, roles, IAM Roles Anywhere; long-term keys "not recommended" |
Borrow, don't own |
aws login |
AWS CLI 2.32.0+; root, IAM users or IAM-federated identities; IAM principals need SignInLocalDevelopmentAccess; refreshed every 15 min for up to 12 h |
|
| Lost secret access key | Delete the key and create a new one (delete one first if the user has two) | |
| Zero-downtime key rotation | Create second key → switch apps → check last use → deactivate old → delete | Overlap, verify, retire |
| Access key last used | Date, service and Region; first use per 15-minute window; tracked since 2015-04-22 | |
sts get-access-key-info |
Returns only the owning account ID, not the key's status | |
| Exposed access keys | AWS may attach AWSCompromisedKeyQuarantineV3 and open a case → replace the key; don't just detach the policy |
|
| Self-managed access keys | Allow iam:*AccessKey* on arn:aws:iam::*:user/${aws:username} |
Credential report and last accessed information
| Topic | Current facts | Memory hook |
|---|---|---|
| Credential report | Account-wide CSV for all users incl. root: passwords, both access key slots, MFA, X.509 certificates | Who holds what |
| Credential report frequency | At most once every 4 hours (a newer report is returned again) | |
| Credential report gaps | No service-specific credentials (CodeCommit Git, Bedrock or CloudWatch Logs API keys); list them with ListServiceSpecificCredentials |
|
| Credential report permissions | iam:GenerateCredentialReport and iam:GetCredentialReport |
|
| Last accessed information | ⚠️ Last Accessed tab (formerly Access Advisor) on users, groups, roles, policies; Organizations entities from the management account | Who used what |
| Last accessed coverage | Within 4 h; services ≥ 400 days; ⚠️ action-level: S3 (2020), EC2/IAM/Lambda (2021), other services since 2023-05-23; management actions only | |
| Last accessed limits | Identity-based policies only; denied attempts included; iam:PassRole never tracked; missing data ≠ unused |
|
| Last accessed API | generate-service-last-accessed-details --granularity ACTION_LEVEL → JobId → get-service-last-accessed-details |
|
| Authoritative record of API calls | CloudTrail |
IAM Access Analyzer findings and policy generation
| Topic | Current facts | Memory hook |
|---|---|---|
| External access analyzer | Finds resource-based policies granting access outside the zone of trust (account or organization); Regional, one per Region | |
| External access resource types | S3 buckets, IAM role trust policies, KMS keys, Lambda, SQS, Secrets Manager, SNS, EBS and RDS snapshots, ECR, EFS, DynamoDB | |
| Finding timing | About 30 min after a policy change; periodic rescan within 24 h; StartResourceScan rescans on demand |
|
| Unused access analyzer | Paid per role and user per month; unused roles, keys, passwords, services and actions (unused age 1–365 days); not Regional | Analyze unused once |
| Internal access analysis | Paid per monitored resource; which principals inside the account or org can reach selected resources; Regional | |
| Policy generation | From a CloudTrail trail over ≤ 90 days; service role trusting access-analyzer.amazonaws.com; no data-event actions (S3 object or DynamoDB item calls such as PutItem), no iam:PassRole; kept 7 days in the console |
Activity → policy |
| Analyzer types | ACCOUNT, ORGANIZATION, ACCOUNT_UNUSED_ACCESS, ORGANIZATION_UNUSED_ACCESS, ACCOUNT_INTERNAL_ACCESS, ORGANIZATION_INTERNAL_ACCESS |
Best practices and shared responsibility
| Topic | Current facts | Memory hook |
|---|---|---|
| AWS side of IAM security | Infrastructure that runs IAM, availability, replication, physical security | Security of the cloud |
| Customer side of IAM security | Identities, policies, MFA, credential lifecycle, least privilege, access reviews | Security in the cloud |
| Human users | ⚠️ Federate through IAM Identity Center or an IdP and use temporary credentials | |
| Workloads | IAM roles; outside AWS: IAM Roles Anywhere, AssumeRoleWithSAML, AssumeRoleWithWebIdentity |
|
| Long-term access keys | ⚠️ Update when needed (e.g. an employee leaves), guided by last-used data, instead of fixed-schedule rotation | |
| Guardrails | SCPs (principals), RCPs (resources) and permissions boundaries limit access but grant nothing | Fences, not gates |
| Policy lifecycle | AWS managed policies → customer managed least privilege → generate, validate, remove unused | |
| IAM Identity Center name | ⚠️ AWS SSO renamed IAM Identity Center on 2022-07-26; aws configure sso and sso* APIs unchanged |
Numbers to know
| Item | Value | Note |
|---|---|---|
| IAM users per account | 5,000 | Not adjustable |
| IAM groups per account | 300 | Adjustable to 500 |
| Groups per IAM user | 10 | Not adjustable |
| Roles per account | 1,000 | ⚠️ Adjustable up to 10,000 |
| Managed policies per user | 10 | Adjustable to 20 |
| Managed policies per group | 10 | Not adjustable |
| Managed policies per role | 20 ⚠️ | Adjustable up to 25 |
| Customer managed policies per account | 1,500 | ⚠️ Adjustable up to 10,000 |
| Managed policy size | 6,144 characters | Whitespace not counted |
| Inline policy size totals | User 2,048 · group 5,120 · role 10,240 characters | Whitespace not counted |
| Role trust policy length | 2,048 characters | ⚠️ Adjustable up to 8,192 |
| Versions per managed policy | 5 | Not adjustable |
| Access keys per IAM user | 2 | Not adjustable |
| MFA devices per user (IAM or root) | 8 ⚠️ | Any mix of types |
| Signing certificates / SSH public keys per user | 2 / 5 | Not adjustable |
| Tags per IAM user or role | 50 | |
| Instance profiles per account | 1,000 | ⚠️ Adjustable up to 10,000; 1 role per instance profile |
| IAM name lengths | User 64 · role 64 · group 128 · policy 128 · path 512 | |
| Role maximum session duration | 1–12 h (default 1 h) | Requests from 900 s |
| Role chaining session | 1 h maximum | |
AssumeRoot session |
≤ 900 s (default 900) | Regional STS endpoint only |
| Root MFA registration window | 35 days | From the first console sign-in attempt |
| Root password | 8–128 characters, ≥ 3 of 4 types | |
| Default IAM password policy | 8–128 characters, ≥ 3 of 4 types, never expires | |
| Custom minimum password length | 6–128 | CLI default 6 when omitted |
| Password expiration | 1–1,095 days | Applies immediately |
| Password reuse prevention | 1–24 previous passwords | |
| Credential report generation | Once every 4 hours | |
| Last accessed data | Within 4 h; services ≥ 400 days | |
| Action-level last accessed tracking start | S3 2020-04-12 · EC2/IAM/Lambda 2021-04-07 · others 2023-05-23 | ⚠️ Management actions only |
| Access key last-used granularity | First use per 15 minutes | Tracked since 2015-04-22 |
RoleLastUsed window |
Trailing 400 days | |
| Access Analyzer policy generation window | ≤ 90 days of CloudTrail | Generated policy kept 7 days in the console |
| Access Analyzer re-analysis | About 30 min after a change | Periodic rescan within 24 h |
| Account alias length | 3–63 characters | Lowercase, not a 12-digit number |
| Access key ID prefixes | AKIA long-term · ASIA temporary |
|
| Unique ID prefixes | AIDA user · AGPA group · AROA role · ANPA managed policy · AIPA instance profile |
|
| IAM error codes | MalformedPolicyDocument 400 · NoSuchEntity 404 · DeleteConflict 409 · LimitExceeded 409 |
|
| Policy language version | 2012-10-17 |
Omitted → 2008-10-17 |
| Region seen for IAM calls | us-east-1 | Matters for aws:RequestedRegion |
Decision rules
- If you see a root-only task (account email, S3 MFA Delete, a policy that denies everyone), think root credentials — or
sts:AssumeRootwith a task policy in a member account. - If you see "remove root credentials from member accounts at scale", think centralized root access management in AWS Organizations.
- If you see root needed from a terminal, think
aws loginwith root credentials, never root access keys. - If you see many IAM users needing the same permissions, think one user group with a customer managed policy.
- If you see a group as
Principalor a group inside a group, think invalid: a group is neither a principal nor a container of groups. - If you see
DeleteConflictfromDeleteUser, think remove keys, MFA, policies, groups and certificates first. - If you see code on AWS compute needing AWS access, think a service role, never an IAM user's keys.
- If you see "the service cannot assume the role", think trust policy, not permissions policy.
- If you see a role under
/aws-service-role/, think service-linked: defined by the service, not editable. - If you see
Principalin an identity-based policy, thinkMalformedPolicyDocument: remove it. - If you see
Describe*/List*scoped to a specific ARN that grants nothing, think"Resource": "*". - If you see
s3:GetObjectfailing with only the bucket ARN, think addbucket/*. - If you see
AllowwithNotAction, think over-granting, including future services. - If you see policy variables used literally, think
Versionmissing or not2012-10-17. - If you see "deny without MFA" that misses access-key calls, think
BoolIfExistsonaws:MultiFactorAuthPresent. - If you see
aws:SourceIpdenying VPC endpoint traffic or CloudFormation calls, think the key is absent or shows the service's IP. - If you see a Region-lock policy breaking IAM, think IAM resolves to us-east-1: exempt global services.
- If you see a sixth policy version rejected, think delete a non-default version (
LimitExceeded, 5 maximum). - If you see "explicit deny in a
<type>policy", think edit that deny; if you see "no<type>policy allows", think add an allow there. - If you see "test a policy without real calls", think policy simulator (cli-sdk-policies); "lint it", think
validate-policy; "block broader access in CI", thinkcheck-no-new-access. - If you see a new role or policy that works only minutes later, think eventual consistency: create IAM resources in an earlier step, wait, retry with backoff.
- If you see one password-policy flag wiping the others, think
update-account-password-policyhas no partial updates. - If you see passkey users needing CLI credentials, think
aws login. - If you see key rotation without downtime, think second key → switch → check last use → deactivate → delete.
- If you see a leaked
AKIA…key, thinksts get-access-key-info, find the user, deactivate and replace. - If you see developers needing CLI access, think IAM Identity Center (
aws configure sso) oraws login, not new access keys. - If you see "which users lack MFA or have old keys", think credential report.
- If you see "which permissions are unused", think last accessed information or an unused access analyzer.
- If you see "who outside the organization can reach this resource", think Access Analyzer external access, one analyzer per Region.
- If you see "least-privilege policy from real activity", think Access Analyzer policy generation (CloudTrail, ≤ 90 days).
Memory hooks
- Break glass, don't carry: root only for root-only tasks.
- No MFA, no root: root MFA is mandatory for every account type.
- 15 minutes of root, one task:
AssumeRoot≤ 900 s with one task policy. - Groups are folders, not people: no credentials, no
Principal, no nesting. - Trust = who, permissions = what.
- Service-linked = service-owned: look, don't touch.
- Always 2012:
"Version": "2012-10-17". - Deny wins, silence denies: explicit deny beats allow; no allow means implicit deny.
- Bucket vs bucket/*: list on the bucket, read and write on the objects.
- Describe needs a star.
- NotAction + Allow = blank cheque.
- IfExists catches keys:
BoolIfExistsfor MFA denies. - IAM lives in us-east-1 for
aws:RequestedRegion. - Explicit deny? Edit the deny. Implicit deny? Add an allow.
- Simulate, validate, gate: simulator →
validate-policy→check-no-new-access. - Send the whole policy: the password-policy CLI has no partial updates.
- Eight is enough: 8 MFA devices per user.
- Passkey to CLI?
aws login. - AKIA = always on (long-term), ASIA = session (temporary).
- Overlap, verify, retire: access key rotation.
- Credential report = who holds what; last accessed = who used what.
- Four-hour report, 400-day memory.
- External analyzer per Region, unused analyzer once.
- Fences, not gates: SCPs, RCPs and boundaries grant nothing.
- Borrow credentials, don't own them: temporary beats long-term.