IAM — DVA-C02 Mastery Guide
- Sources: course chapter 01 (IAM). The slides serve as the syllabus only; all wording here is original.
- Written 2026-10-12; values checked against AWS docs on 2026-10-12. The marker
⚠️ Changed: <current>. Older material may say <old>.flags a value or behaviour that differs from the course or from older practice tests: learn the current value.⚠️ unverifiedmarks anything that could not be confirmed in AWS documentation. Every change is listed in Changed vs older material; rows enternotes/current-facts.mdonce you confirm them.- Tier 1 (highest exam weight), bank target 30 questions. Domain: Security.
- Scope: users, groups and roles; policy JSON structure (Effect, Action, Resource, Condition); MFA and access keys; IAM security tools (credential report, last accessed information, IAM Access Analyzer); shared responsibility. Covered elsewhere: STS (
AssumeRole, session durations), cross-account access,iam:PassRole, the full policy evaluation logic, inline vs managed policies and policy variables →notes/advanced-identity-guide.md; CLI profiles, credential provider chains, MFA with the CLI (sts get-session-token), SigV4, the IAM policy simulator, dry runs, readingAccessDeniedmessages anddecode-authorization-message→notes/cli-sdk-policies-guide.md§11–§12; instance profiles and IMDS →notes/ec2-mastery-guide.md§1–§2.- How to use it:
/explain <topic>teaches from these sections (concept → exam facts → developer angle → traps → 3 check questions);/quiz iamasks bank questions whosesource_notelinks point at the## N.headings below;/drill iamfires short recall prompts fromnotes/iam-cheat-sheet.md. Suggested loop: read a section, answer its practice questions before opening the answers,/explainwhat is still unclear, then/quiz iamand/drill iam.
How IAM appears on DVA-C02
IAM is the authorization layer behind every other service on the exam, so it appears both as its own topic and inside Lambda, S3, DynamoDB or CI/CD scenarios. A question describes code, a pipeline or a person that is blocked, over-privileged or holding the wrong kind of credential, and asks for the fix. What you must get right:
- Which identity is calling: root user, IAM user or role session; long-term
AKIA…keys or temporaryASIA…credentials. - What a policy really grants: reading Effect, Action, Resource and Condition, and spotting a statement that grants nothing or far too much.
- Least privilege under a constraint: roles instead of keys, temporary instead of long-term credentials, scoped ARNs, conditions on MFA, source IP, Region, TLS or tags.
- Troubleshooting: reading an
AccessDeniedmessage, simulating and validating a policy, allowing for eventual consistency. - Auditing: choosing between the credential report, last accessed information, IAM Access Analyzer and CloudTrail.
Typical question shapes: a scenario with one deciding constraint ("MOST secure", "least privilege", "LEAST operational overhead"); a policy excerpt or an error message to diagnose; "Which TWO…" with five options; the exact API action, CLI command, condition key or setting that does the job.
Domain map: the bank for this module is tagged Security. Scenarios often borrow Development framing (an SDK call fails with AccessDenied) or Troubleshooting framing (§6, §10).
Out of scope here: AWS Organizations design beyond the guardrail facts in §1 and §12, directory services, and network design (only the condition keys that mention VPC endpoints or IP ranges appear).
| # | Topic | Why it is on the exam |
|---|---|---|
| 1 | Root user and account protection | Root-only tasks, mandatory root MFA, no root keys, unlocking resources with centralized root access |
| 2 | IAM users and user groups | When an IAM user is still the right identity, how groups grant permissions, quotas, the DeleteConflict cleanup |
| 3 | IAM roles for AWS services | Trust policy vs permissions policy, service roles vs service-linked roles: how code on AWS gets permissions |
| 4 | Policy document structure | Effect, Action, Resource and Principal; statements that grant nothing or too much; managed policy versions |
| 5 | Policy conditions and condition keys | MFA, source IP, Region, TLS and tag conditions; missing-key behaviour and ...IfExists |
| 6 | Testing and troubleshooting policies | Access Analyzer validation and custom checks as CI gates, eventual consistency, console policy warnings |
| 7 | Password policy | Default vs custom rules, what applies immediately, the CLI's no-partial-update trap |
| 8 | MFA devices and enforcement | Passkeys first, 8 devices per user, MFA for CLI calls, enforcing MFA in policies |
| 9 | Access keys and programmatic access | Temporary credentials first; key anatomy, zero-downtime rotation, last-used data, leaked keys |
| 10 | Credential report and last accessed information | Which audit tool answers which question; report contents and tracking periods |
| 11 | IAM Access Analyzer findings and policy generation | External, internal and unused access findings; least-privilege policies drafted from CloudTrail |
| 12 | Best practices and shared responsibility | The current AWS IAM best-practice list and who secures what |
1. Root user and account protection
Concept
- The root user is the identity that exists from the moment an account is created: the account's email address plus a password. It has complete access to every resource and setting, and identity-based policies cannot be attached to it.
- Treat root as a break-glass identity. Daily administration happens through an administrative user in IAM Identity Center (or a role); root is reserved for the short list of tasks nothing else can perform.
- Protecting root means MFA (now mandatory), a strong unique password, no access keys, a mailbox your organization controls, recovery channels (email inbox and phone number) held by different people, and an alert whenever root signs in.
- With AWS Organizations you can go further and remove member accounts' root credentials altogether. The management account, or a delegated administrator account for IAM, then runs the rare root-only fixes as short, task-scoped sessions.
Exam facts
- Root-only tasks (current list, condensed):
- Standalone accounts: change the account email address, the root password or root access keys; close the account; reopen a closed account during the post-closure period.
- Restore permissions when the only IAM administrator has locked themselves out.
- Activate IAM user and role access to the Billing and Cost Management console; view certain tax invoices.
- Configure S3 MFA Delete; edit or delete an S3 bucket policy or an SQS queue policy that denies all principals.
- Register as a seller in the EC2 Reserved Instance Marketplace; sign up for AWS GovCloud (US).
- ⚠️ Changed: in standalone accounts only the email address, root password and root access keys need root; account name, contact details, alternate contacts, payment currency and Regions can be changed by authorized IAM principals. Older material may say that only root can change the account name or the Support plan.
- ⚠️ Changed: MFA is required for the root user of every account type (standalone, Organizations management and member accounts); a root user without MFA must register a device within 35 days of its first console sign-in attempt. Older material may say root MFA is only a recommended best practice.
- Root can register up to 8 MFA devices of any type; if all of them are lost, recovery goes through the account's email address and phone number.
- Root password rules: 8–128 characters, at least 3 of uppercase, lowercase, digits and symbols, not identical to the account name or email address. The IAM account password policy (§7) does not apply to root.
- No root access keys. When a root-only task has to run from a terminal,
aws loginwith the root credentials gives temporary, automatically rotated credentials instead. - ⚠️ Changed: centralized root access for Organizations member accounts (since Nov 15, 2024) deletes member root passwords, access keys and signing certificates and deactivates their MFA; with the feature on, new member accounts start without root credentials, and root-only fixes run as privileged sessions. Older material may say every member account keeps its own root credentials, each secured with its own MFA.
- Privileged root sessions (
sts:AssumeRoot): called by an IAM user or role in the management account or the delegated IAM administrator account (root credentials cannot call it); Regional STS endpoint only (the global endpoint is not supported);DurationSecondsup to 900 (default 900, i.e. 15 minutes);TaskPolicyArnmust be one of five AWS managed task policies:IAMAuditRootUserCredentials,IAMCreateRootUserPassword,IAMDeleteRootUserCredentials,S3UnlockBucketPolicy,SQSUnlockQueuePolicy. The condition keysts:TaskPolicyArnlimits which task an operator may start. STS mechanics: advanced-identity guide. - The privileged tasks: delete a misconfigured bucket policy, delete a misconfigured SQS queue policy, delete member root credentials, and Allow password recovery (only when the member has no root credentials) so the mailbox owner can reset the root password.
- If member accounts keep root credentials, an SCP can deny root actions there except the root-only ones (SCPs: advanced-identity guide).
- Watching root: CloudTrail records root sign-ins and
AssumeRootsessions as distinct events; alert with an EventBridge rule plus SNS, or with GuardDuty; AWS Config rulesroot-account-mfa-enabledandiam-root-access-key-check; Security Hub CSPM and Trusted Advisor also flag root without MFA (monitoring guide).
Developer angle
Unlock a bucket that a bad policy has closed to everyone in member account 111122223333, working from the delegated IAM administrator account:
# 1) task-scoped root session for the member account (Regional endpoint, at most 900 s)
aws sts assume-root --region eu-west-1 \
--target-principal 111122223333 \
--task-policy-arn arn=arn:aws:iam::aws:policy/root-task/S3UnlockBucketPolicy \
--duration-seconds 900 \
--query Credentials --output json > root-session.json
# 2) use the session, then fix the policy properly as a normal admin afterwards
export AWS_ACCESS_KEY_ID=$(jq -r .AccessKeyId root-session.json)
export AWS_SECRET_ACCESS_KEY=$(jq -r .SecretAccessKey root-session.json)
export AWS_SESSION_TOKEN=$(jq -r .SessionToken root-session.json)
aws s3api delete-bucket-policy --bucket locked-reports-bucket
- Audit root credentials without signing in as root: the root row of the credential report shows
mfa_active,access_key_1_activeandaccess_key_2_active(§10). - A request to the global endpoint
sts.amazonaws.comor a--duration-secondsabove 900 makesassume-rootfail; pin the Region in the profile or on the command line.
Traps
- Trap: "Use root for automated deployments because it can never be denied." → Reality: root is reserved for root-only tasks; deployments run as least-privilege roles.
- Trap: "Restrict root with an IAM policy or a permissions boundary." → Reality: neither attaches to root; only an SCP can restrict the root user of a member account.
- Trap: "Create root access keys so a script can close accounts." → Reality: no root keys; use
aws loginfor occasional root CLI work, or close member accounts from the Organizations management account. - Trap: "A bucket policy that denies every principal can never be undone." → Reality: the owning account's root user can delete it; with centralized root access,
AssumeRootwithS3UnlockBucketPolicydoes it without any root credentials. - Trap: "Request a one-hour
AssumeRootsession throughsts.amazonaws.com." → Reality: 900 s maximum, Regional endpoint only. - Trap: "The account password policy also hardens the root password." → Reality: it governs IAM users only.
- Trap: "Root MFA is optional when the password is strong." → Reality: root MFA is enforced for every account type.
Practice questions
-
A company uses AWS Organizations with centralized root access enabled, and the root credentials of all member accounts have been deleted. In member account 111122223333, a faulty bucket policy now denies every principal access to a bucket. A developer in the delegated IAM administrator account must remove the policy with the LEAST operational overhead. What should the developer do?
- A. Run the Allow password recovery task, reset the member root password, sign in as root and delete the bucket policy.
- B. Call
sts:AssumeRooton a Regional endpoint with theS3UnlockBucketPolicytask policy, then delete the bucket policy with the session credentials. - C. Assume the
OrganizationAccountAccessRolerole in the member account and runaws s3api delete-bucket-policy. - D. Attach an SCP to the member account that allows
s3:DeleteBucketPolicyon the bucket.
-
A developer is hardening a new standalone AWS account that hosts a side project. Which TWO actions follow AWS guidance for the root user? (Select TWO.)
- A. Register a passkey or a FIDO security key as an MFA device for the root user.
- B. Create root access keys and store them in AWS Secrets Manager for automation jobs.
- C. Set a custom IAM account password policy that requires 20-character passwords for the root user.
- D. Create an administrative identity for everyday work and keep the root credentials for root-only tasks.
- E. Attach a permissions boundary to the root user to cap what it can do.
Answers
- B. A privileged session scoped by
S3UnlockBucketPolicyis exactly what centralized root access provides. A works but re-creates root credentials and adds manual steps; C fails because the bucket policy's deny also matches the administrator role; D fails because an SCP never grants permissions and cannot override the bucket's explicit deny. - A, D. Phishing-resistant MFA and a separate administrative identity are root best practices. B: AWS recommends never creating root access keys; C: the password policy applies only to IAM users; E: permissions boundaries attach to IAM users and roles, never to root.
2. IAM users and user groups
Concept
- An IAM user is a long-term identity inside one account: a name plus whatever credentials you give it — a console password, up to two access keys, X.509 signing certificates, SSH public keys and service-specific credentials (CodeCommit Git credentials, Amazon Keyspaces). It keeps those credentials until someone removes them.
- ⚠️ Changed: AWS wants people to federate (IAM Identity Center or an external identity provider) and work with temporary credentials. IAM users remain for what federation cannot cover: workloads that cannot use roles, third-party tools without Identity Center support, CodeCommit SSH or Git credentials, Keyspaces service-specific credentials and emergency (break-glass) access. Older material may say that every person should get their own IAM user.
- A user group attaches identity-based policies once for many users. It is not an identity: it has no credentials, cannot sign in, cannot assume roles and cannot be named as a
Principal. - A user's identity-based permissions are the union of the policies attached to the user (inline and managed) and to every group it belongs to; an explicit
Denyin any of them still beats everyAllow. How the other policy types join the evaluation: advanced-identity guide.
Exam facts
- Quotas: 5,000 users per account (not adjustable); 300 groups per account (adjustable to 500); each user joins at most 10 groups (not adjustable); 10 managed policies per user (adjustable to 20) and per group (not adjustable); inline policies total 2,048 characters per user and 5,120 per group (whitespace not counted); 50 tags per user.
- Groups cannot be nested, and there is no built-in group that contains every user.
- A group cannot appear as a
Principalin a resource-based policy (bucket policy, role trust policy): it is a permissions container, never an authenticated caller. Grant the group an identity-based policy instead, or name users or roles. - Same account: either an identity-based policy or a resource-based policy can allow a request, so a group policy alone is enough for a bucket in the same account. Cross-account access needs both sides (advanced-identity guide).
- Names are unique within the account and compared case-insensitively (no
Adminsnext toadmins): user name up to 64 characters, group name up to 128, path up to 512. At sign-in a user name is case-insensitive; in ARNs and policies it is case-sensitive. - ARNs have an empty Region field because IAM resources are global:
arn:aws:iam::111122223333:user/division_a/alice,arn:aws:iam::111122223333:group/Developers. Paths can't be set in the console (use the API, CLI, SDKs or CloudFormation), and a path never puts anyone in a group. - Unique IDs: users
AIDA…, groupsAGPA…(returned by the CLI and API, not shown in the console). A deleted and recreated user gets a new ID: a role trust policy that named the old user stores its ID and stops matching until you re-save the ARN, while identity policies that match by name (for example ahome/${aws:username}/*prefix) work for the newcomer at once. - Deleting a user with the CLI or API requires removing first: login profile (password), access keys, signing certificates, SSH public keys, service-specific credentials, MFA devices, inline policies, attached managed policies and group memberships. Anything left behind →
DeleteConflict(HTTP 409). The console removes most of these for you, once active access keys are deactivated. - Suspend without deleting: attach a deny-all policy, optionally limited in time with
aws:CurrentTime. - Safe onboarding: generated console password with "must create a new password at next sign-in", permissions granted only after the first successful sign-in, then an MFA-required policy (§8).
- Sign-in URL uses the account ID or an account alias (3–63 characters, lowercase, not a 12-digit number).
Developer angle
aws iam create-group --group-name Backend
aws iam attach-group-policy --group-name Backend \
--policy-arn arn:aws:iam::111122223333:policy/backend-data-access
aws iam create-user --user-name build-agent-legacy --tags Key=owner,Value=platform
aws iam add-user-to-group --user-name build-agent-legacy --group-name Backend
aws iam list-groups-for-user --user-name build-agent-legacy
aws iam list-attached-user-policies --user-name build-agent-legacy # managed policies
aws iam list-user-policies --user-name build-agent-legacy # inline policy names
aws iam get-account-authorization-details --filter User Group # one dump for audits
aws iam get-account-summary # usage vs IAM quotas
Removing a user in code means emptying it first (boto3):
import boto3
iam = boto3.client("iam")
def delete_user(name):
for key in iam.list_access_keys(UserName=name)["AccessKeyMetadata"]:
iam.delete_access_key(UserName=name, AccessKeyId=key["AccessKeyId"])
for device in iam.list_mfa_devices(UserName=name)["MFADevices"]:
iam.deactivate_mfa_device(UserName=name, SerialNumber=device["SerialNumber"])
for policy in iam.list_attached_user_policies(UserName=name)["AttachedPolicies"]:
iam.detach_user_policy(UserName=name, PolicyArn=policy["PolicyArn"])
for policy_name in iam.list_user_policies(UserName=name)["PolicyNames"]:
iam.delete_user_policy(UserName=name, PolicyName=policy_name)
for group in iam.list_groups_for_user(UserName=name)["Groups"]:
iam.remove_user_from_group(UserName=name, GroupName=group["GroupName"])
try:
iam.delete_login_profile(UserName=name)
except iam.exceptions.NoSuchEntityException:
pass # the user never had a console password
try:
iam.delete_user(UserName=name)
except iam.exceptions.DeleteConflictException as err:
print("still attached:", err) # e.g. signing certificates, SSH keys, Git credentials
- A deactivated virtual MFA device becomes unassigned; delete it with
delete_virtual_mfa_device(SerialNumber=...)if it should not be reused.
Traps
- Trap: "Nest the
Backendgroup inside anEngineeringgroup." → Reality: IAM has no nested groups; members are always IAM users. - Trap: "Name the group's ARN as
Principalin the bucket policy." → Reality: groups can't be principals; use the group's identity policy (same account) or role and user ARNs. - Trap: "Give each of 6,000 application customers an IAM user." → Reality: 5,000-user hard limit, and application end users belong in Amazon Cognito (advanced-identity guide).
- Trap: "Removing a user from a group also removes the deny attached to the user." → Reality: the user's own policies stay; group membership only adds the group's policies.
- Trap: "
DeleteUserfailing withDeleteConflictis a permissions problem." → Reality: subordinate items are still attached; a permissions problem returnsAccessDenied. - Trap: "A recreated user with the old name can never reach the old user's data." → Reality: name-based identity policies match the newcomer; only principal references in trust and resource policies are pinned to the old unique ID.
Practice questions
-
A developer writes a cleanup job that calls
DeleteUserfor contractors who have left the company. The calls fail withDeleteConflict, although the job's role has full IAM permissions. What is the MOST likely cause?- A. IAM is eventually consistent, so the users are not yet visible to the job.
- B. The users belong to a group whose policy contains an explicit
Denyforiam:DeleteUser. - C. The users still have attached items such as access keys, MFA devices or managed policies.
- D. The job's role lacks
iam:PassRolefor the users being deleted.
-
A team lead must give 12 backend developers, who sign in as IAM users, identical read/write access to one DynamoDB table and one S3 bucket in the same account, with the LEAST operational overhead. Which TWO steps meet the requirement? (Select TWO.)
- A. Add
arn:aws:iam::111122223333:group/Backendas thePrincipalin the bucket policy. - B. Create a
Backenduser group, attach one customer managed policy with the table and bucket permissions, and add the 12 users. - C. Nest the
Backendgroup inside the existingEngineeringgroup. - D. Attach an identical inline policy to each of the 12 users.
- E. Keep the current bucket policy as it is, without adding a statement for the developers.
- A. Add
Answers
- C.
DeleteConflict(HTTP 409) means subordinate entities are still attached; the CLI and API never remove them for you. A would surface asNoSuchEntityrather than a conflict; B and D would produceAccessDenied, andiam:PassRolehas nothing to do with deleting users. - B, E. One managed policy on a group is the low-overhead pattern, and within one account the identity-based allow is sufficient. A fails because a group can't be a principal; C fails because groups can't be nested; D works but multiplies maintenance across 12 copies.
3. IAM roles for AWS services
Concept
- A role is an identity with permissions but without a password or access keys. Whoever is trusted to assume it receives temporary credentials for a session. For code that runs on AWS — EC2 instances, Lambda functions, ECS tasks, CodeBuild projects, CloudFormation stacks — a role is the standard way to get permissions.
- Every role has two halves that must agree:
- the trust policy, a resource-based policy on the role that says which principals may assume it (
Principalplussts:AssumeRole); - one or more permissions policies, identity-based, that say what the session may do. Trust without permissions lets the service in but allows nothing; permissions without trust are unreachable.
- the trust policy, a resource-based policy on the role that says which principals may assume it (
- Service role: a role that a service assumes on your behalf; you create and edit it in IAM (Lambda execution role, CloudFormation service role, CodeBuild service role).
- Service-linked role: a special service role created for, defined by and owned by one service; you can view it but cannot change its permissions.
- Configuring a resource with a role passes the role to the service, which is checked through
iam:PassRole: advanced-identity guide, andnotes/ec2-mastery-guide.md§1 for the instance profile case.
Exam facts
- Service principals look like
<service>.amazonaws.com(ec2.amazonaws.com,lambda.amazonaws.com,cloudformation.amazonaws.com); several services go into an array under a singleServicekey;"Service": "*"is not allowed; trust policies should use the non-Regional form. - Service roles you meet on DVA-C02: EC2 instance role through an instance profile (one role per profile, ec2 guide §1); Lambda execution role (lambda guide); CloudFormation service role passed with
--role-arn(cloudformation guide); CodeBuild, CodeDeploy and CodePipeline service roles (cicd guide); ECS task role vs task execution role (containers guide). - Service-linked roles: path
/aws-service-role/<service principal>/, names usually starting withAWSServiceRoleFor; the service defines the trust and permissions policies and, unless it says otherwise, only that service can assume the role; usually created automatically, otherwiseaws iam create-service-linked-role --aws-service-name <principal>(needsiam:CreateServiceLinkedRole); deletable only after the service's dependent resources are gone; they count toward the role quota yet can still be created when the account is at the limit; permissions boundaries cannot be applied to them, and IAM calls that try to change them fail withUnmodifiableEntity(HTTP 400). - Quotas: 1,000 roles and 1,000 instance profiles per account; role trust policy 2,048 characters; inline role policies total 10,240 characters; role name up to 64 characters (path plus name ≤ 64 to use console Switch Role).
- ⚠️ Changed: 20 managed policies per role by default, adjustable up to 25. Older material may say 10, adjustable to 20.
- ⚠️ Changed: the auto-approved maximums are 10,000 roles, 10,000 instance profiles and an 8,192-character trust policy (defaults unchanged). Older material may say 5,000 / 5,000 / 4,096.
- Sessions: a role's maximum session duration is 1–12 hours (default 1 hour); callers may request from 900 seconds up to that maximum; role chaining caps a session at 1 hour. The setting does not limit sessions that AWS services assume for you, such as EC2 instance role credentials. Details: advanced-identity guide.
- Role IDs start with
AROA; a session ARN looks likearn:aws:sts::111122223333:assumed-role/<role-name>/<session-name>; theaws:PrincipalArncondition key returns the role ARN, not the session ARN. - A trust policy that names a specific user or role stores that principal's unique ID: delete and recreate the principal and the trust breaks until you re-save the ARN.
aws iam get-rolereturnsRoleLastUsed(date and Region, trailing 400 days), the quickest way to spot dead roles (§10).
Developer angle
Create a Lambda execution role: trust first, then permissions.
cat > lambda-trust.json <<'EOF'
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "Service": "lambda.amazonaws.com" },
"Action": "sts:AssumeRole"
}]
}
EOF
aws iam create-role --role-name orders-fn-role \
--assume-role-policy-document file://lambda-trust.json \
--max-session-duration 3600
aws iam attach-role-policy --role-name orders-fn-role \
--policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
aws iam put-role-policy --role-name orders-fn-role --policy-name orders-table-rw \
--policy-document file://orders-table-rw.json
aws iam update-assume-role-policy --role-name orders-fn-role \
--policy-document file://lambda-trust.json # replaces a wrong trust policy in place
aws iam get-role --role-name orders-fn-role --query Role.RoleLastUsed
aws iam list-roles --path-prefix /aws-service-role/ --query 'Roles[].RoleName'
- Code never handles role credentials itself: create SDK clients without keys (
boto3.client("dynamodb")) and the default provider chain finds the function's or instance's role (cli-sdk-policies guide). - Lambda rejects a function whose execution role does not trust
lambda.amazonaws.com, because the service cannot assume it: fix the trust policy, not the permissions.
Traps
- Trap: "Put the allowed DynamoDB actions into the trust policy." → Reality: the trust policy says who may assume the role; the permissions policies say what the session may do.
- Trap: "Edit the service-linked role to add the S3 access the service needs." → Reality: its permissions belong to the service; use a service role you own, or the service's own configuration.
- Trap: "Delete unused service-linked roles to clean up." → Reality: remove the service's dependent resources first; deletion goes through the service or
DeleteServiceLinkedRole. - Trap: "Trust every service with
"Service": "*"." → Reality: not allowed; list the exact service principals. - Trap: "Add the role to a user group so the team inherits it." → Reality: a role can't be a group member; grant the group's users
sts:AssumeRoleon it (advanced-identity guide). - Trap: "A 1-hour maximum session duration makes the EC2 instance's role credentials expire." → Reality: that setting does not limit sessions AWS services assume; instance credentials rotate automatically.
- Trap: "Store an IAM user's access keys in the function's environment variables." → Reality: the execution role already supplies temporary credentials.
Practice questions
-
A developer creates an IAM role whose permissions policy allows
dynamodb:PutItemon theOrderstable, then selects it as the execution role of a new Lambda function. Lambda rejects the role because the service cannot assume it. Which change fixes the problem?- A. Allow
sts:AssumeRolefor thelambda.amazonaws.comservice principal in the role's trust policy. - B. Add
dynamodb:*on all resources to the role's permissions policy. - C. Increase the role's maximum session duration to 12 hours.
- D. Attach the
AWSLambdaBasicExecutionRolemanaged policy to the role.
- A. Allow
-
While reviewing roles, a developer finds
AWSServiceRoleForElasticLoadBalancingunder the path/aws-service-role/and wants to reduce its permissions. Which TWO statements are correct? (Select TWO.)- A. Its permissions are defined by Elastic Load Balancing and cannot be edited in IAM.
- B. A permissions boundary can be attached to it to cap its permissions.
- C. It can be deleted only after the load balancer resources that depend on it are gone.
- D. Any principal in the account that has
sts:AssumeRolepermission can assume it. - E. It does not count toward the account's quota of IAM roles.
Answers
- A. The role's trust policy must name the Lambda service principal; permissions are irrelevant until the service can assume the role. B only broadens permissions; C changes session length, not who may assume the role; D adds CloudWatch Logs permissions but still no trust.
- A, C. Service-linked roles are defined and owned by their service and are protected while resources depend on them. B is wrong because boundaries can't be applied to service-linked roles; D is wrong because only the linked service assumes them; E is wrong because they count toward the quota (they may merely exceed it).
4. Policy document structure
Concept
- A policy is one JSON object:
Versionplus exactly oneStatementelement whose value is a statement object or an array of them; resource-based policies may also carry anId. - Each statement answers four questions: allow or deny (
Effect), which API actions (ActionorNotAction), on which resources (ResourceorNotResource) and, optionally, under which circumstances (Condition). Resource-based policies add a fifth: for whom (PrincipalorNotPrincipal). - Identity-based policies attach to users, groups and roles; the principal is whoever carries the policy, so there is no
Principalelement. Resource-based policies attach to a resource (S3 bucket, SQS queue, KMS key, Lambda function, and a role's trust policy) and must say whom they apply to. - Default deny: nothing is allowed unless a statement allows it (implicit deny), and a matching
Denybeats any number ofAllows (explicit deny). How identity, resource, boundary, session and organization policies combine: advanced-identity guide.
Exam facts
Version: write"2012-10-17", the current policy language version. Leave it out and IAM falls back to2008-10-17, where policy variables such as${aws:username}are compared as literal text. It is neither the policy's edit date nor a managed-policy version.Statement: one per policy, with several statements in an array. Each statement carries exactly oneEffect, oneAction/NotActionand oneResource/NotResourcekey; duplicated keys are rejected in new policies.Sidis an optional statement label (IAM policies accept only letters and digits in it).Idis an optional policy label that only resource-based policies may contain (SQS and SNS use it); identity-based policies must not include it.Action:service-prefix:ActionName; prefix and name are case-insensitive;*and?wildcards (ec2:Describe*,iam:*AccessKey*); an array for several actions.Resource: ARNs of the formarn:partition:service:region:account-id:resource; IAM ARNs leave the Region empty; wildcards may appear in any segment. An incomplete ARN in an identity-based policy is completed with wildcards:arn:aws:sqsbehaves likearn:aws:sqs:*:*:*, every queue everywhere.- Actions without resource-level permissions (many
Describe*andList*calls,s3:ListAllMyBuckets,dynamodb:ListTables) need"Resource": "*"; paired with a specific ARN, the statement grants nothing. - S3 needs two ARN shapes: bucket actions (
s3:ListBucket) onarn:aws:s3:::bucket, object actions (s3:GetObject,s3:PutObject) onarn:aws:s3:::bucket/*(s3 guide). Principal(resource-based policies only): an account ("AWS": "111122223333"orarn:aws:iam::111122223333:root, both meaning the whole account rather than just its root user), a user or role ARN, a service ("Service": "lambda.amazonaws.com"), a federated provider, or"*". Principal ARNs accept no partial wildcards, and groups are never principals."Principal": "*"withAllowgrants public access.NotPrincipal: only withDeny, and not in identity-based policies, trust policies, SCPs or RCPs; AWS recommends aDenywithArnNotEqualsonaws:PrincipalArninstead.NotActionwithAllowallows every action that is not listed, within theResourcescope:"NotAction": "iam:*"with"Resource": "*"grants every non-IAM action, including services launched later.NotActionwithDenydenies everything except the listed actions — the usual guardrail shape (§5, §8).- Size limits: customer managed policy 6,144 characters; inline policies total 2,048 per user, 5,120 per group and 10,240 per role (whitespace excluded); policy name up to 128 characters.
- ⚠️ Changed: customer managed policies per account: 1,500 by default, adjustable up to 10,000. Older material may give 5,000 as the maximum.
- Managed policy versions: up to 5 stored versions per customer managed policy; exactly one is the default, in effect wherever the policy is attached; roll back with
set-default-policy-version; through the CLI or API you must delete a non-default version before creating a sixth (LimitExceeded, HTTP 409), while the console asks which one to drop; the default version can't be deleted; inline policies have no versions; AWS sets the default version of AWS managed policies. To freeze a policy, deny bothiam:CreatePolicyVersionandiam:SetDefaultPolicyVersion. - Error codes:
MalformedPolicyDocument(HTTP 400) for grammar problems such as aPrincipalinside an identity-based policy;LimitExceeded(409) for version and size quotas;NoSuchEntity(404) for a wrong name or ARN. - When to choose inline or managed policies: advanced-identity guide.
Developer angle
A least-privilege identity policy for an order-reporting service:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ReadOrdersTable",
"Effect": "Allow",
"Action": ["dynamodb:GetItem", "dynamodb:Query"],
"Resource": "arn:aws:dynamodb:eu-west-1:111122223333:table/Orders"
},
{ "Sid": "ListTablesNeedsStar", "Effect": "Allow", "Action": "dynamodb:ListTables", "Resource": "*" },
{
"Sid": "ListReportPrefix",
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::reports-bucket",
"Condition": { "StringLike": { "s3:prefix": "2026/*" } }
},
{
"Sid": "ReadWriteReportObjects",
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:PutObject"],
"Resource": "arn:aws:s3:::reports-bucket/2026/*"
},
{ "Sid": "NeverDropTables", "Effect": "Deny", "Action": "dynamodb:DeleteTable", "Resource": "*" }
]
}
aws iam create-policy --policy-name orders-reporting --policy-document file://orders-reporting.json
aws iam create-policy-version \
--policy-arn arn:aws:iam::111122223333:policy/orders-reporting \
--policy-document file://orders-reporting-v2.json --set-as-default
aws iam list-policy-versions --policy-arn arn:aws:iam::111122223333:policy/orders-reporting
aws iam set-default-policy-version \
--policy-arn arn:aws:iam::111122223333:policy/orders-reporting --version-id v1 # roll back
aws iam delete-policy-version \
--policy-arn arn:aws:iam::111122223333:policy/orders-reporting --version-id v2
- IAM may store a policy in restructured form (reordered keys, account IDs in principals turned into root ARNs), so never compare policy documents as strings in code or tests.
Traps
- Trap: "Add a
Principalto the role's permissions policy to make it apply to the role." → Reality: identity-based policies may not containPrincipal; IAM rejects them as malformed. - Trap: "
ec2:Describe*scoped to one instance ARN lets the app describe that instance." → Reality: Describe calls have no resource-level permissions; with a specific ARN the statement grants nothing. - Trap: "
s3:GetObjectonarn:aws:s3:::bucketcovers the objects." → Reality: object actions needarn:aws:s3:::bucket/*. - Trap: "
Allow+NotAction: iam:*is a tidy read-only policy." → Reality: it grants every non-IAM action, now and for future services. - Trap: "Omitting
Versionis harmless." → Reality: the 2008 language applies and${aws:username}stops working. - Trap: "Give every identity policy an
Idfor traceability." → Reality:Idis for resource-based policies; label statements withSidinstead. - Trap: "
arn:aws:iam::111122223333:rootasPrincipaltrusts only that account's root user." → Reality: it trusts the account; administrators there still decide which identities may act. - Trap: "A more specific
Allowoverrides a broadDeny." → Reality: an explicit deny always wins.
Practice questions
-
A developer attaches this statement to a role:
{"Effect": "Allow", "Action": ["s3:ListBucket", "s3:GetObject"], "Resource": "arn:aws:s3:::reports-bucket"}. Listing the bucket works, but downloading objects fails withAccessDenied. What should the developer change?- A. Set
"Version"to"2008-10-17"in the policy. - B. Add a
Principalelement that names the role ARN. - C. Replace
ActionwithNotAction, keeping the same two actions. - D. Add
arn:aws:s3:::reports-bucket/*to the statement'sResourcelist.
- A. Set
-
A developer tries to save the following identity-based policy for a role, expecting it to let the role describe EC2 instances:
{ "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::111122223333:role/app-role" }, "Action": "ec2:DescribeInstances", "Resource": "arn:aws:ec2:eu-west-1:111122223333:instance/i-0abc1234def567890" }] }Which TWO changes are needed? (Select TWO.)
- A. Change
Versionto2008-10-17. - B. Replace
PrincipalwithNotPrincipal. - C. Remove the
Principalelement. - D. Add a
Sidto the statement. - E. Set
Resourceto"*"forec2:DescribeInstances.
- A. Change
Answers
- D.
s3:GetObjectacts on object ARNs, so the statement needsreports-bucket/*. A switches to the old language without changing ARN matching; B is invalid in an identity-based policy; C would allow every other S3 action on the bucket ARN instead. - C, E. IAM rejects a
Principalin an identity-based policy, andDescribeInstanceshas no resource-level permissions, so it needs"*". A breaks policy variables and fixes nothing; B is invalid outside resource-based policies; D is optional metadata.
5. Policy conditions and condition keys
Concept
Conditionnarrows when a statement applies:operator → key → value(s). Keys come from the request context: globalaws:keys (who, from where, how, when) and service keys (s3:prefix,ec2:InstanceType).- Combination logic: several operators → AND; several keys under one operator → AND; several values for one key → OR; several values under a negated operator (
StringNotEquals,ArnNotLike,NotIpAddress) → NOR, so none of them may match. - A missing key makes most conditions false (the statement does not apply); a negated operator is true when its key is missing;
...IfExiststreats a missing key as true;Nulltests whether the key is present. - Two roles in practice: guardrails in
Denystatements (no MFA, no TLS, wrong Region, wrong network) and scoping inAllowstatements (only these tags, only this prefix, only recent MFA).
Exam facts
| Operator family | Operators | Typical keys |
|---|---|---|
| String | StringEquals, StringNotEquals, StringEqualsIgnoreCase, StringLike / StringNotLike (* and ?) |
aws:PrincipalTag/team, s3:prefix |
| Numeric | NumericEquals, NumericLessThan(Equals), NumericGreaterThan(Equals) |
aws:MultiFactorAuthAge, s3:max-keys |
| Date | DateEquals, DateLessThan(Equals), DateGreaterThan(Equals) (ISO 8601 or epoch) |
aws:CurrentTime, aws:EpochTime, aws:TokenIssueTime |
| Boolean | Bool |
aws:SecureTransport, aws:MultiFactorAuthPresent |
| IP address | IpAddress, NotIpAddress (CIDR, IPv4 or IPv6; an address without a prefix gets the default /32, so give a single IPv6 address an explicit /128) |
aws:SourceIp |
| ARN | ArnEquals = ArnLike, ArnNotEquals = ArnNotLike |
aws:SourceArn, aws:PrincipalArn |
| Binary | BinaryEquals (base64 value) |
binary keys |
| Existence | <operator>IfExists, Null |
any key |
| Multivalued | ForAnyValue: / ForAllValues: prefixes |
aws:TagKeys, aws:CalledVia |
aws:MultiFactorAuthPresentistruein MFA-authenticated temporary sessions and absent when a request is signed with long-term access keys. A "deny without MFA" statement therefore needs"BoolIfExists": {"aws:MultiFactorAuthPresent": "false"}; plainBoolwith"false"never matches access-key calls, so they slip through.aws:MultiFactorAuthAge: seconds since the MFA sign-in; present only with temporary credentials (an allow onNumericLessThan 3600therefore never matches long-term keys).aws:SourceIp: the caller's public IP address (only public ranges are meaningful); absent for requests that come through a VPC endpoint (useaws:VpcSourceIp,aws:SourceVpceoraws:SourceVpcthere, vpc guide); when a service calls another service on your behalf (CloudFormation calling EC2), the target sees the calling service's address, so an IP-based deny breaks those calls.aws:RequestedRegion: the Region of the endpoint called. IAM has a single endpoint in us-east-1, so IAM calls always carryus-east-1; a "deny outside eu-west-1" statement must exempt global services withNotActionor it breaks IAM.aws:SecureTransportis always present:"Bool": {"aws:SecureTransport": "false"}in aDenyblocks plain-HTTP requests (classic bucket policy).- Tags (ABAC):
aws:PrincipalTag/<key>,aws:ResourceTag/<key>,aws:RequestTag/<key>,aws:TagKeys; in the.../<key>forms the tag key matches case-insensitively and the tag value case-sensitively. aws:PrincipalArn(role ARN for role sessions),aws:PrincipalOrgID(requester's organization),aws:ViaAWSServiceandaws:CalledVia(requests a service makes with your credentials),aws:CurrentTime(time windows).ForAllValues:evaluates to true when the key is missing or empty; add aNullcheck with"false"so a request without tags can't pass.- Condition key names are case-insensitive;
StringEqualsvalues are case-sensitive. - Only
StringLikeandStringNotLike(plus the ARN operators) treat*and?as wildcards; inStringEqualsthey are literal characters. - When one statement covers several resource types (
ec2:RunInstancestouches instances, images, security groups…), a key that exists for only one of them needsStringLikeIfExistsor the whole statement fails. - Policy variables (
${aws:username},${aws:PrincipalTag/team}) needVersion2012-10-17; designing dynamic policies: advanced-identity guide.
Developer angle
Guardrails and scoping in one identity policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "OnlyEuWest1ExceptGlobalServices",
"Effect": "Deny",
"NotAction": ["iam:*", "sts:*", "organizations:*", "support:*"],
"Resource": "*",
"Condition": { "StringNotEquals": { "aws:RequestedRegion": "eu-west-1" } }
},
{
"Sid": "StartStopOwnTeamInstances",
"Effect": "Allow",
"Action": ["ec2:StartInstances", "ec2:StopInstances"],
"Resource": "arn:aws:ec2:eu-west-1:111122223333:instance/*",
"Condition": { "StringEquals": { "aws:ResourceTag/team": "${aws:PrincipalTag/team}" } }
},
{
"Sid": "OwnKeysOnlyWithRecentMfa",
"Effect": "Allow",
"Action": ["iam:CreateAccessKey", "iam:DeleteAccessKey"],
"Resource": "arn:aws:iam::111122223333:user/${aws:username}",
"Condition": { "NumericLessThan": { "aws:MultiFactorAuthAge": "3600" } }
}
]
}
A bucket policy that refuses plain HTTP:
{
"Version": "2012-10-17",
"Statement": [{
"Sid": "DenyPlainHttp",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": ["arn:aws:s3:::invoices-bucket", "arn:aws:s3:::invoices-bucket/*"],
"Condition": { "Bool": { "aws:SecureTransport": "false" } }
}]
}
- Test conditions before deploying by passing context values to the simulator, e.g.
--context-entries ContextKeyName=aws:MultiFactorAuthPresent,ContextKeyValues=false,ContextKeyType=boolean(§6).
Traps
- Trap: "
Bool+aws:MultiFactorAuthPresent: falsein aDenyblocks every non-MFA call." → Reality: access-key calls carry no such key; useBoolIfExists. - Trap: "Allow the corporate private range
10.0.0.0/8withaws:SourceIp." → Reality: the key holds public addresses, and it is absent through VPC endpoints. - Trap: "Region-lock everything with
aws:RequestedRegion." → Reality: IAM and other global services resolve to us-east-1; exempt them or IAM calls fail. - Trap: "
ForAllValues:StringEqualsonaws:TagKeysforces callers to tag." → Reality: a request without tags passes; add aNullcheck. - Trap: "Two values under one key mean both must match." → Reality: values are ORed; use two keys (or two operators) for AND.
- Trap: "
StringEqualswithteam-*matchesteam-blue." → Reality: wildcards work only in the...Likeand ARN operators; useStringLike.
Practice questions
-
A security team requires that IAM users can call no AWS API, other than setting up their own MFA, unless they authenticated with MFA. This must also apply to CLI calls signed with long-term access keys. The current deny statement uses
"Bool": {"aws:MultiFactorAuthPresent": "false"}, yet CLI calls made with access keys still succeed. What should the developer change?- A. Replace the condition with
"NumericGreaterThan": {"aws:MultiFactorAuthAge": "3600"}. - B. Change the operator to
BoolIfExistsand keep the value"false". - C. Change the value to
"true"and switch the statement's effect toAllow. - D. Add
"Bool": {"aws:SecureTransport": "false"}to the same condition block.
- A. Replace the condition with
-
A developer attaches a deny statement with
"NotIpAddress": {"aws:SourceIp": "198.51.100.0/24"}to a role. The role is used from laptops on the corporate network, by EC2 instances in private subnets that reach S3 through a gateway VPC endpoint, and by a CloudFormation stack that creates EC2 resources with the role's credentials. Laptop calls work, but the instances' S3 calls and the stack's EC2 calls are denied. Which TWO explanations are correct? (Select TWO.)- A.
aws:SourceIpaccepts only IPv6 ranges, so the IPv4 CIDR never matches. - B. Requests that arrive through a VPC endpoint carry no
aws:SourceIpvalue, so the negated condition is true and the deny applies. - C.
aws:SourceIpis evaluated only in resource-based policies, so in a role policy it denies everything. - D. When CloudFormation calls EC2 on the developer's behalf, EC2 sees CloudFormation's address instead of the corporate range.
- E. The statement must use
StringNotEqualsbecauseNotIpAddressis not a valid operator.
- A.
Answers
- B. With
BoolIfExists, a missing key counts as true, so access-key calls (no MFA key) and non-MFA sessions (false) are both denied. A fails because the age key is also absent for long-term keys; C would not stop other policies from allowing non-MFA calls; D tests TLS, not MFA. - B, D. VPC endpoint traffic has no
aws:SourceIp, and service-made calls show the service's address. A, C and E are false: the key supports IPv4 and IPv6, works in identity-based policies, andNotIpAddressis the correct operator.
6. Testing and troubleshooting policies
Concept
- Before a policy ships, IAM Access Analyzer answers two questions: is it well formed and free of risky patterns (policy validation), and does it meet your own rule — no broader than the deployed version, never a critical action, never public (custom policy checks). "Would this exact request be allowed?" is the IAM policy simulator's job (cli-sdk-policies guide §11).
- After deployment, a denied call is diagnosed from its
AccessDeniedmessage andaws sts get-caller-identity; message formats anddecode-authorization-messageare in the cli-sdk-policies guide §12. - IAM is eventually consistent: every change is written to a single control plane in us-east-1 and then copied to a read-only data plane in each enabled Region, so a new role or policy can be invisible for a short while.
Exam facts
- Policy validation (
aws accessanalyzer validate-policy, APIValidatePolicy): more than 100 checks against policy grammar and AWS best practices. Finding types:ERROR(the policy won't work as written),SECURITY_WARNING(overly permissive access),WARNING(not best practice),SUGGESTION(an improvement that doesn't change permissions). --policy-typeIDENTITY_POLICY,RESOURCE_POLICY,SERVICE_CONTROL_POLICYorRESOURCE_CONTROL_POLICY; for resource policies,--validate-policy-resource-type(for exampleAWS::S3::Bucket,AWS::IAM::AssumeRolePolicyDocument) adds service-specific checks.- The console JSON editor shows validation findings for managed policies and for user or role inline policies (not group inline policies); a user without
access-analyzer:ValidatePolicysees a "You need permissions" notice there instead. - Custom policy checks (charged per check):
check-no-new-access(new document vs the existing one),check-access-not-granted(the listed actions and/or resources must not be allowed),check-no-public-access(a resource policy for a given--resource-typecan't grant public access). ResultPASS, orFAIL= might allow, with the statement that caused it; the first two takeIDENTITY_POLICYorRESOURCE_POLICY. The console's Check for new access tab runs the first one while you edit. These are the usual CI/CD gates. - A clean validation is not proof of least privilege: it judges syntax and patterns, custom checks compare against what you specify, and generation from real activity is §11.
- Simulator and messages in one line each (owned by the cli-sdk-policies guide): the simulator evaluates identity policies, boundaries, SCPs and one resource policy you supply, without calling the service; "because no
<type>policy allows" is an implicit deny (add an allow there), "with an explicit deny in a<type>policy" is an explicit deny (change thatDeny; no allow can win). - Eventual consistency: users, groups, roles, policies and tags (ABAC) take time to reach every endpoint, and IAM also caches. Keep IAM creates and updates out of critical, high-availability code paths: make them in a setup or deployment step, confirm propagation before production depends on them, and wrap the first use in a retry with backoff.
- Console policy summary warnings: "This policy does not grant any permissions" = an action paired with a resource or condition it doesn't support (§4); "defines some actions, resources, or conditions that do not provide permissions" = part of the policy is dead. Policies with
Principal,NotPrincipalorNotResourceget no summary at all. - Policy variables that don't resolve → check
"Version": "2012-10-17"and the variable's spelling and case.
Developer angle
A CI stage that lints and gates policy changes before deployment:
# lint an identity policy draft (fail the build on ERROR or SECURITY_WARNING)
aws accessanalyzer validate-policy --policy-type IDENTITY_POLICY \
--policy-document file://draft.json --query 'findings[].[findingType,issueCode]'
# lint a role trust policy with the trust-policy-specific checks
aws accessanalyzer validate-policy --policy-type RESOURCE_POLICY \
--validate-policy-resource-type AWS::IAM::AssumeRolePolicyDocument \
--policy-document file://trust.json
# gate 1: no broader than what is deployed today (prints PASS or FAIL)
aws accessanalyzer check-no-new-access --policy-type IDENTITY_POLICY \
--existing-policy-document file://deployed.json --new-policy-document file://draft.json \
--query result --output text
# gate 2: never grant these critical actions
aws accessanalyzer check-access-not-granted --policy-type IDENTITY_POLICY \
--policy-document file://draft.json \
--access '[{"actions": ["iam:CreateUser", "s3:DeleteBucket"]}]'
# gate 3: the bucket policy must not allow public access
aws accessanalyzer check-no-public-access --resource-type AWS::S3::Bucket \
--policy-document file://bucket-policy.json
When a deployment script creates a role and uses it straight away, wait for it and retry the first use:
aws iam create-role --role-name orders-fn-role \
--assume-role-policy-document file://lambda-trust.json
aws iam wait role-exists --role-name orders-fn-role # GetRole sees it; other endpoints may still lag
# then retry the first call that uses the role (e.g. lambda create-function) with backoff
Traps
- Trap: "
validate-policyreturned no findings, so the policy is least privilege." → Reality: it judges grammar and patterns; use custom checks or policy generation (§11) to judge access. - Trap: "
check-no-new-accessreturnedFAIL, so the new policy is broken." → Reality:FAILmeans it might grant more than the reference version; review the reported statement. - Trap: "Add an
Allowto beat 'explicit deny in a service control policy'." → Reality: no allow overrides an explicit deny; change the SCP's deny. - Trap: "Create a role and use it in the same request path a second later." → Reality: eventual consistency; create it during deployment, wait, and retry with backoff.
- Trap: "
aws iam wait role-existsguarantees every service can use the role." → Reality: it only shows thatGetRolereturns the role; other endpoints can still lag, so keep a retry on first use. - Trap: "The console says the policy grants no permissions, so IAM rejected it." → Reality: it was saved; an action is paired with a resource or condition it doesn't support.
Practice questions
-
A team stores a role's customer managed policy in Git. The CI pipeline must block any merge that would grant more access than the currently deployed version, with the LEAST operational overhead. What should the developer add to the pipeline?
- A.
aws accessanalyzer validate-policy, failing the build on anyWARNINGfinding. - B.
aws iam simulate-custom-policyfor every action of every service against the new document. - C.
aws accessanalyzer check-no-new-accesswith the deployed and proposed documents, failing onFAIL. - D. A post-deployment review of CloudTrail for newly allowed calls during the following week.
- A.
-
A deployment script creates an IAM role and immediately creates a Lambda function that uses it. The first
CreateFunctioncall sometimes fails because Lambda cannot assume the role, yet the same call succeeds when the script is rerun a minute later. The trust policy is correct. Which TWO changes make the deployment reliable? (Select TWO.)- A. Wait with
aws iam wait role-exists, then retryCreateFunctionwith exponential backoff. - B. Create the role in an earlier deployment stage than the function.
- C. Add
lambda:*on all resources to the role's permissions policy. - D. Change the trust policy principal to
"Service": "*"for all services. - E. Raise the role's maximum session duration to 12 hours.
- A. Wait with
Answers
- C.
check-no-new-accesscompares two versions and returnsFAILwhen the new one grants more, which is exactly the gate required. A checks grammar and best practices, not added access; B is impractical; D detects the problem only after deployment. - A, B. The failure is IAM eventual consistency: the new role has not reached every endpoint yet, so waiting and retrying, or creating roles ahead of time, fixes it. C changes what the function may do, not whether Lambda can assume the role; D is not allowed in a trust policy; E only changes session length.
7. Password policy
Concept
- The account password policy sets the rules for IAM users' console passwords: length, character classes, expiry, reuse, whether users may change their own password, and whether an expired password needs an administrator. It does not touch the root user or any access keys.
- Without a custom policy, a default AWS policy applies. Custom settings are created and changed through the console, the CLI or the API.
- AWS's answer to password guessing is length and complexity rules combined with MFA; IAM has no lockout setting.
Exam facts
- Default policy (no custom policy set): 8–128 characters, at least 3 of uppercase, lowercase, numbers and symbols, not identical to the account name or email address, never expires.
- Custom options:
- minimum length 6–128 characters;
- require at least one uppercase letter, lowercase letter, number and/or non-alphanumeric character (each optional);
- expiration after 1–1,095 days;
- prevent reuse of the last 1–24 passwords;
- Password expiration requires administrator reset (hard expiry): users cannot renew an expired password in the console; an administrator with
iam:UpdateLoginProfileresets it, or the user resets it programmatically withiam:ChangePasswordand active access keys; - Allow users to change their own password: an account-level grant of
iam:ChangePasswordon the user's own identity plusiam:GetAccountPasswordPolicy; no policy is attached to anyone.
- Timing: length and character rules apply the next time each user changes a password (existing passwords are not forced); an expiration period applies immediately — with 90 days, everyone whose password is older must change it at the next sign-in, and passwords 75–89 days old trigger a console warning.
- An expired password blocks console sign-in only; the user's access keys keep working.
- No lockout policy exists: you can't lock users out after failed sign-in attempts.
- Permissions to manage it:
iam:GetAccountPasswordPolicy,iam:UpdateAccountPasswordPolicy,iam:DeleteAccountPasswordPolicy(deleting reverts to the default policy). - CLI trap:
update-account-password-policydoes not support partial updates. Every option you leave out reverts to its parameter default, not to the default policy above — an omitted--minimum-password-lengthbecomes 6, an omitted--max-password-agemeans passwords never expire, omitted--require-*flags become false. - Before enabling hard expiry, make sure more than one administrator can reset passwords.
- The credential report shows
password_last_changedandpassword_next_rotationper user (§10).
Developer angle
# always send the complete policy: omitted options fall back to defaults
aws iam update-account-password-policy \
--minimum-password-length 14 \
--require-uppercase-characters --require-lowercase-characters \
--require-numbers --require-symbols \
--max-password-age 90 --password-reuse-prevention 24 \
--allow-users-to-change-password
aws iam get-account-password-policy
aws iam delete-account-password-policy # back to the AWS default policy
# an administrator resets a console password and forces a change at next sign-in
aws iam update-login-profile --user-name alice \
--password 'Temp-Reset#2026-Q4' --password-reset-required
Change one setting safely from code by copying every active setting back (boto3):
import boto3
iam = boto3.client("iam")
SETTABLE = {"MinimumPasswordLength", "RequireSymbols", "RequireNumbers",
"RequireUppercaseCharacters", "RequireLowercaseCharacters",
"AllowUsersToChangePassword", "MaxPasswordAge",
"PasswordReusePrevention", "HardExpiry"}
try:
current = iam.get_account_password_policy()["PasswordPolicy"]
except iam.exceptions.NoSuchEntityException:
current = {} # no custom policy yet
# copy every active setting (ExpirePasswords is read-only and skipped); anything left out
# reverts to its default, i.e. false or "not set", exactly what a false or 0 value meant
settings = {key: value for key, value in current.items() if key in SETTABLE and value}
settings["RequireSymbols"] = True # the one setting being changed
iam.update_account_password_policy(**settings)
Traps
- Trap: "Run
update-account-password-policy --require-symbolsto add one rule." → Reality: all other settings fall back to their defaults. - Trap: "Raising the minimum length forces everyone to change passwords now." → Reality: length and character rules apply at each user's next change; only expiry is immediate.
- Trap: "An expired console password also disables the user's CLI access." → Reality: access keys are unaffected.
- Trap: "Lock accounts after five failed sign-ins." → Reality: IAM has no lockout policy; rely on MFA.
- Trap: "The password policy also governs the root password." → Reality: root has its own fixed rules (§1).
- Trap: "'Allow users to change their own password' attaches a policy to each user." → Reality: it is an account-level grant.
Practice questions
-
A deployment script runs
aws iam update-account-password-policy --require-symbolsto add a symbol requirement. Afterwards, the minimum length has dropped from 14 to 6 characters and passwords no longer expire. What explains this?- A. The operation does not support partial updates, so every omitted option reverted to its default.
- B. IAM is eventually consistent, so the previous values will reappear after propagation.
- C. The default AWS password policy overrides custom settings once symbols are required.
- D. Requiring symbols resets the length and expiry rules because they conflict.
-
An administrator changes the account password policy: the minimum length rises from 8 to 14 characters and passwords now expire after 60 days. Which TWO outcomes are expected? (Select TWO.)
- A. Users' access keys stop working as soon as their console passwords expire.
- B. Users whose current password is older than 60 days must set a new one at their next console sign-in.
- C. Users with passwords shorter than 14 characters may keep them until their next password change.
- D. The root user's password also expires after 60 days.
- E. Users who fail to sign in five times are locked out for 30 minutes.
Answers
- A.
UpdateAccountPasswordPolicyreplaces the whole policy, so options left out return to their defaults (length 6, no expiry). B is wrong because the change is not a propagation delay; C and D describe behaviour that does not exist. - B, C. Expiration applies immediately, while length and character rules apply at the next change. A: expired passwords leave access keys working; D: the policy never applies to root; E: IAM has no lockout setting.
8. MFA devices and enforcement
Concept
- MFA adds a possession or biometric factor (a passkey, a security key, a one-time-code generator) to the password, so a leaked password is no longer enough to sign in.
- Device types, strongest first: passkeys and security keys (FIDO, phishing-resistant, recommended), virtual authenticator apps (TOTP codes), hardware TOTP tokens. SMS can no longer be enabled.
- MFA reaches API calls only through temporary credentials: a session requested from STS with a TOTP code carries the MFA context, and policies check it with
aws:MultiFactorAuthPresentandaws:MultiFactorAuthAge(§5). Passkey and security key users get CLI and SDK credentials throughaws logininstead. - Root MFA is enforced by AWS (§1); for IAM users, you register devices and enforce MFA through policies.
Exam facts
- ⚠️ Changed: up to 8 MFA devices per IAM user and per root user, any mix of types (since Nov 16, 2022); one registered device is enough to sign in. Older material may say one MFA device per user.
- ⚠️ Changed: passkeys, synced through credential managers or bound to a device such as a YubiKey security key, are supported since June 11, 2024, and AWS recommends phishing-resistant FIDO MFA wherever possible. Older material may describe only "U2F security keys".
- A single FIDO security key may be registered for several identities, root and IAM alike; a TOTP token or authenticator-app entry belongs to exactly one user.
- ⚠️ Changed: hardware TOTP tokens work only if their seeds are shared with AWS: Thales OTP tokens or OTP display cards bought through the links AWS publishes, and Hypersecu OTP tokens for AWS GovCloud (US). Older material may name Gemalto key fobs and SurePassID (GovCloud).
- ⚠️ Changed: SMS text-message MFA can no longer be enabled; move remaining SMS users to another type. Older material may list SMS as an option.
- Passkeys and security keys can be registered only in the console (not the CLI or API), and their MFA information cannot be passed to STS operations such as
GetSessionToken; to get CLI/SDK credentials with a passkey, runaws login. - Adding or removing an extra MFA device requires authenticating with an existing one; a lost or stolen device should be removed from the user.
- A virtual device's serial number is its ARN,
arn:aws:iam::<account-id>:mfa/<device-name>; a hardware token's is printed on the device. Device names must be unique in the account. enable-mfa-devicetakes two consecutive codes; submit them immediately or the device goes out of sync (resync-mfa-device).- A cancelled console setup leaves an unassigned virtual device behind. Adding a virtual device in the console or during sign-in normally deletes such devices, which frees their names. When assigning or removing a device fails with "not authorized to perform: iam:DeleteVirtualMFADevice … with an explicit deny", an administrator deletes the leftover device with the CLI or API; editing policies is not the fix.
- MFA for the CLI with TOTP codes (
aws sts get-session-token --serial-number … --token-code …) and profiles: cli-sdk-policies guide. Requiring MFA to assume a cross-account role: advanced-identity guide. - Checking status: console MFA column (Virtual, Security key, Hardware, SMS);
aws iam list-mfa-devices; credential reportmfa_active. - Identity Center users get MFA through IAM Identity Center's own settings; S3 MFA Delete is a root-only bucket setting (s3 guide).
Developer angle
Register a virtual device from the CLI (passkeys and security keys need the console):
aws iam create-virtual-mfa-device --virtual-mfa-device-name alice-phone \
--outfile alice-qr.png --bootstrap-method QRCodePNG
aws iam enable-mfa-device --user-name alice \
--serial-number arn:aws:iam::111122223333:mfa/alice-phone \
--authentication-code1 123456 --authentication-code2 789012
aws iam list-mfa-devices --user-name alice
aws iam list-virtual-mfa-devices --assignment-status Unassigned
aws iam deactivate-mfa-device --user-name alice \
--serial-number arn:aws:iam::111122223333:mfa/alice-phone
aws iam delete-virtual-mfa-device --serial-number arn:aws:iam::111122223333:mfa/alice-phone
Enforce MFA while still letting users enrol their first device:
{
"Version": "2012-10-17",
"Statement": [
{ "Sid": "ListDevicesNeedsStar", "Effect": "Allow", "Action": "iam:ListVirtualMFADevices", "Resource": "*" },
{ "Sid": "CreateOwnVirtualDevice", "Effect": "Allow", "Action": "iam:CreateVirtualMFADevice", "Resource": "arn:aws:iam::*:mfa/*" },
{
"Sid": "ManageOwnUserMfa",
"Effect": "Allow",
"Action": ["iam:EnableMFADevice", "iam:ResyncMFADevice", "iam:ListMFADevices", "iam:GetUser"],
"Resource": "arn:aws:iam::*:user/${aws:username}"
},
{
"Sid": "BlockOtherCallsWithoutMfa",
"Effect": "Deny",
"NotAction": ["iam:CreateVirtualMFADevice", "iam:EnableMFADevice", "iam:ResyncMFADevice",
"iam:ListMFADevices", "iam:ListVirtualMFADevices", "iam:GetUser",
"sts:GetSessionToken"],
"Resource": "*",
"Condition": { "BoolIfExists": { "aws:MultiFactorAuthPresent": "false" } }
}
]
}
Traps
- Trap: "Pass the passkey to
get-session-tokenfor MFA-protected CLI calls." → Reality: FIDO MFA can't be passed to STS; useaws login, or a TOTP device withget-session-token. - Trap: "Register the new security key with
aws iam enable-mfa-device." → Reality: passkeys and security keys are console-only. - Trap: "A user can have one MFA device, so the backup must replace it." → Reality: up to 8 devices per user.
- Trap: "Enable SMS MFA for users without smartphones apps." → Reality: SMS can no longer be enabled; use a hardware token or security key.
- Trap: "Root MFA also protects the account's IAM users." → Reality: each identity has its own MFA configuration.
- Trap: "Fix the
iam:DeleteVirtualMFADeviceexplicit-deny error by editing the MFA policy." → Reality: delete the leftover unassigned virtual device with the CLI or API.
Practice questions
-
Developers who are IAM users sign in to the console with passwords and passkeys. They now need temporary credentials for the AWS CLI, without anyone creating access keys, with the LEAST operational overhead. What should they do?
- A. Run
aws sts get-session-tokenwith the passkey's ARN as--serial-numberand a code produced by the passkey. - B. Create access keys for each developer and exempt those keys from the MFA policy.
- C. Register each passkey again with
aws iam enable-mfa-device. - D. Run
aws loginand complete the browser sign-in with the console password and passkey.
- A. Run
-
The only FIDO security key of an IAM administrator broke. A year ago, the administrator also registered a virtual authenticator app on the same IAM user. Which TWO statements are correct? (Select TWO.)
- A. The administrator must complete the AWS account recovery process with the account's email address and phone number.
- B. An IAM user can hold one MFA device only, so the app registration replaced the security key.
- C. A replacement security key can be registered with
aws iam enable-mfa-device. - D. The administrator can still sign in by entering a code from the virtual authenticator app.
- E. After signing in with the app, the administrator can remove the broken security key from the user.
Answers
- D.
aws loginturns the console sign-in, passkey included, into temporary CLI credentials (the IAM users need theSignInLocalDevelopmentAccessmanaged policy). A fails because FIDO MFA can't be passed to STS; B breaks the no-access-keys requirement; C fails because passkeys can only be registered in the console. - D, E. Any one of up to eight registered devices signs the user in, and authenticating with an existing device allows removing another. A applies to root users who lost every device; B is outdated; C fails because security keys are console-only.
9. Access keys and programmatic access
Concept
- An access key is a pair: the access key ID (
AKIA…, not secret, shown in the console) and the secret access key (shown once). Together they sign API requests (SigV4: cli-sdk-policies guide). They are long-term: valid until deactivated or deleted. - Temporary credentials (
ASIA…key ID + secret + session token, with an expiry) are what roles, IAM Identity Center andaws loginhand out, and they are the preferred choice wherever possible. - Key lifecycle: create (secret visible once) → use → monitor last use → rotate with a two-key overlap → deactivate → delete.
Exam facts
- 2 access keys per IAM user, not adjustable — the second slot exists for zero-downtime rotation.
- The secret is retrievable only at creation. Lost secret → delete the key and create a new one (delete one first if the user already has two).
AKIA= long-term key of an IAM user or the root user;ASIA= temporary STS credentials, which also need the session token.- ⚠️ Changed: AWS labels long-term access keys "not recommended" for programmatic access and lists alternatives:
aws loginwith console credentials (AWS CLI 2.32.0 or later; 15-minute credentials refreshed for up to 12 h), IAM Identity Center (aws configure sso), AWS CloudShell, IAM roles for compute (EC2, Lambda, ECS), IAM Roles Anywhere for servers outside AWS. Older material may say "create access keys for CLI and SDK access" (same fact as the ec2 row innotes/current-facts.md). aws loginworks for the root user, IAM users and IAM-federated identities (Identity Center users useaws configure sso); IAM principals need the AWS managed policySignInLocalDevelopmentAccess, root needs nothing extra;aws logoutdeletes the cached credentials.- Long-term keys remain for workloads that can't use roles and third-party tools without Identity Center support (§2).
- Key states:
ActiveorInactive; an inactive key can be reactivated, a deleted key is gone. - Zero-downtime rotation: create a second key → move every application to it → check the old key's last use with
get-access-key-last-used(waiting a few days helps) → deactivate the old key → confirm nothing broke (reactivate if something did) → delete it. - Last used: date, service and Region; only the first use in each 15-minute window is recorded; tracked since April 22, 2015. The console's Access key age column shows the age of a user's oldest active key.
- Self-service key management needs an explicit grant, typically
iam:*AccessKey*onarn:aws:iam::*:user/${aws:username}. aws sts get-access-key-info --access-key-id <id>returns the owning account ID (triage for keys found in code); it says nothing about whether the key is active.- Exposed keys: AWS may attach the managed policy
AWSCompromisedKeyQuarantineV3to the user, denying many high-risk actions, and open a support case. Rotate or delete the key and follow the case; don't simply detach the policy. - Never place keys in code, repositories, AMIs, container images or committed
.envfiles; the shared credentials file stores them in plaintext. Third-party secrets belong in Secrets Manager (security-encryption guide). - An expired console password doesn't affect access keys (§7). The credential report covers both key slots (§10).
- Other IAM user credentials are not access keys: SSH public keys for CodeCommit (5 per user), X.509 signing certificates (2 per user), service-specific credentials (CodeCommit Git, Keyspaces, Amazon Bedrock and CloudWatch Logs long-term API keys).
Developer angle
aws iam create-access-key --user-name legacy-sync # the only time the secret is returned
aws iam list-access-keys --user-name legacy-sync \
--query 'AccessKeyMetadata[].[AccessKeyId,Status,CreateDate]' --output table
aws iam get-access-key-last-used --access-key-id AKIAIOSFODNN7EXAMPLE
aws iam update-access-key --user-name legacy-sync \
--access-key-id AKIAIOSFODNN7EXAMPLE --status Inactive
aws iam delete-access-key --user-name legacy-sync --access-key-id AKIAIOSFODNN7EXAMPLE
aws sts get-access-key-info --access-key-id AKIAIOSFODNN7EXAMPLE # which account owns it?
Find old active keys and their last use (boto3); the 90-day threshold is an example, not an AWS rule:
from datetime import datetime, timedelta, timezone
import boto3
iam = boto3.client("iam")
cutoff = datetime.now(timezone.utc) - timedelta(days=90)
for page in iam.get_paginator("list_users").paginate():
for user in page["Users"]:
for key in iam.list_access_keys(UserName=user["UserName"])["AccessKeyMetadata"]:
if key["Status"] == "Active" and key["CreateDate"] < cutoff:
last = iam.get_access_key_last_used(AccessKeyId=key["AccessKeyId"])
used = last["AccessKeyLastUsed"].get("LastUsedDate", "never")
print(user["UserName"], key["AccessKeyId"], key["CreateDate"].date(), used)
Traps
- Trap: "Download the secret again from the console." → Reality: it is shown only at creation; create a new key.
- Trap: "Create a third key during rotation." → Reality: two per user; delete or rotate within the two slots.
- Trap: "Delete the old key as soon as the new one exists." → Reality: deactivate first, confirm, then delete.
- Trap: "An
ASIA…key ID and secret are enough to sign requests." → Reality: temporary credentials also need the session token. - Trap: "Rotate the access keys used by a Lambda function." → Reality: a function uses its execution role; it should have no keys.
- Trap: "Detach
AWSCompromisedKeyQuarantineV3to get the app running again." → Reality: replace the exposed key and follow the support case. - Trap: "
get-access-key-infotells whether a leaked key is still active." → Reality: it only returns the account ID.
Practice questions
-
An on-premises batch job signs requests with the access key of an IAM user. Security asks for the key to be rotated without interrupting the job. What should the developer do FIRST?
- A. Deactivate the current key, then create a replacement and update the job.
- B. Create a second access key while the first stays active, then move the job to it.
- C. Retrieve the current secret access key and store it in AWS Secrets Manager.
- D. Delete the current key, then create a new one and update the job.
-
A developer learns that an access key ID beginning with
AKIAwas pushed to a public Git repository. Which TWO actions are appropriate? (Select TWO.)- A. Run
aws sts get-access-key-infoon the key ID to confirm which AWS account owns it. - B. Detach the
AWSCompromisedKeyQuarantineV3policy that AWS attached to the user. - C. Identify the owning IAM user (credential report or
list-access-keys), then deactivate and replace the key. - D. Change the IAM user's console password, which invalidates the user's access keys.
- E. Wait 12 hours, after which
AKIAkeys expire automatically.
- A. Run
Answers
- B. The second key slot exists for overlap: create, switch, verify, then deactivate and delete the old key. A and D interrupt the job before the replacement is in place; C is impossible because the secret is shown only at creation.
- A, C. Confirm the owning account, find the user, then replace the key. B removes AWS's protective controls instead of fixing the exposure; D fails because passwords and access keys are independent; E fails because
AKIAkeys never expire.
10. Credential report and last accessed information
Concept
- IAM has two built-in audit views that answer different questions:
- the credential report (account-wide CSV): which credentials exist and how old and how recently used they are — passwords, access keys, MFA and signing certificates for every user, root included;
- last accessed information (formerly called Access Advisor): which services, and for many services which management actions, an identity or policy is allowed and actually used, and when — the input for removing unused permissions.
- CloudTrail remains the authoritative record of individual API calls (monitoring guide), and IAM Access Analyzer turns unused-access analysis into continuous findings (§11).
| You need to know… | Use |
|---|---|
| Which users have a password but no MFA, old keys or unused keys | Credential report |
| Which services and actions a user, group, role or policy actually used | Last accessed information |
| Who outside the account or organization can reach a resource | Access Analyzer external access (§11) |
| Unused roles, keys, passwords and permissions across the organization, continuously | Access Analyzer unused access (§11) |
| Every call a principal made, when, from where and with what result | CloudTrail |
| Whether a policy would allow a given call | Policy simulator (§6) |
Exam facts
- Credential report: can be generated at most once every 4 hours (a newer report is simply returned again); AWS stores one report; CSV format; needs
iam:GenerateCredentialReportandiam:GetCredentialReport. - Columns:
user,arn,user_creation_time,password_enabled,password_last_used,password_last_changed,password_next_rotation,mfa_active,access_key_1_active,access_key_1_last_rotated,access_key_1_last_used_date,access_key_1_last_used_region,access_key_1_last_used_service, the same five for key 2,cert_1_active,cert_1_last_rotated, the same for certificate 2, andadditional_credentials_info. - It covers passwords, both access key slots, MFA devices and X.509 signing certificates — not service-specific credentials (CodeCommit Git passwords, Bedrock or CloudWatch Logs long-term API keys); list those with
ListServiceSpecificCredentials. N/Aorno_informationmean: no such credential, never used, or used only before tracking began (password use since Oct 20, 2014; access key use since April 22, 2015). For the root row,password_next_rotationis alwaysnot_supported.- Last accessed information: console Last Accessed tab on users, user groups, roles and policies; from the Organizations management account also for the root, OUs and accounts (services allowed by SCPs).
- ⚠️ Changed: action-level data covers management actions of many services: Amazon S3 since April 12, 2020; EC2, IAM and Lambda since April 7, 2021; all other supported services since May 23, 2023 (data-plane events are not included). Older material may say "Access Advisor shows service-level data for a user" with action detail for S3 only.
- Recent activity appears within 4 hours; service data covers at least 400 days; every attempt is reported, including denied ones; only identity-based policies are considered (resource-based policies, SCPs, boundaries and session policies are excluded);
iam:PassRoleis never tracked. - API:
GenerateServiceLastAccessedDetails(--granularity SERVICE_LEVELorACTION_LEVEL) returns aJobId→GetServiceLastAccessedDetails,GetServiceLastAccessedDetailsWithEntities;ListPoliciesGrantingServiceAccessshows which policies grant a service. Only the principal that generated a report can read it (same session for role credentials). - Per-identity shortcuts:
RoleLastUsedfromget-role(date and Region, trailing 400 days),PasswordLastUsedfromget-user,GetAccessKeyLastUsedfor keys. - Missing tracking data is not proof of non-use (not every action is tracked): use the data to inform least privilege, then test.
Developer angle
aws iam generate-credential-report # State: STARTED → COMPLETE
aws iam get-credential-report --query Content --output text | base64 --decode > credential-report.csv
JOB=$(aws iam generate-service-last-accessed-details \
--arn arn:aws:iam::111122223333:role/orders-fn-role --granularity ACTION_LEVEL \
--query JobId --output text)
aws iam get-service-last-accessed-details --job-id "$JOB" \
--query 'ServicesLastAccessed[].[ServiceNamespace,LastAuthenticated]' --output table
# repeat the get call until JobStatus is COMPLETED (it starts as IN_PROGRESS)
Flag console users without MFA straight from the report (boto3 returns the CSV as raw bytes):
import csv
import io
import time
import boto3
iam = boto3.client("iam")
while iam.generate_credential_report()["State"] != "COMPLETE":
time.sleep(2)
report = iam.get_credential_report()["Content"].decode("utf-8")
for row in csv.DictReader(io.StringIO(report)):
if row["password_enabled"].lower() == "true" and row["mfa_active"].lower() == "false":
print("console password without MFA:", row["user"])
Traps
- Trap: "Regenerate the credential report every 15 minutes for near-real-time data." → Reality: at most once every 4 hours.
- Trap: "The credential report lists every credential a user holds." → Reality: only passwords, two keys, MFA and certificates; service-specific credentials are missing.
- Trap: "Use last accessed information as the audit trail of API calls." → Reality: CloudTrail is the authoritative source.
- Trap: "Last accessed data shows access granted by bucket policies." → Reality: it reflects identity-based policies only.
- Trap: "An action missing from the last accessed list is safe to remove." → Reality: it may simply not be tracked; verify before removing.
- Trap: "Open the Access Advisor tab." → Reality: the console tab is now Last Accessed.
Practice questions
-
A compliance team needs a list of every IAM user who has a console password but no MFA device, plus every access key that has not been used for 90 days, with the LEAST effort. What should a developer use?
- A. The IAM credential report, filtered on
password_enabled,mfa_activeand theaccess_key_*_last_used_datecolumns. - B. Service last accessed details generated for each user at action level.
- C. A CloudTrail Lake query per user covering the last 90 days.
- D. An IAM Access Analyzer external access analyzer for the account.
- A. The IAM credential report, filtered on
-
A developer wants to trim a role's broad managed policy down to what the application actually uses. Which TWO approaches help? (Select TWO.)
- A. Download the credential report and remove the services the role has not used.
- B. Generate action-level last accessed details for the role and review unused services and actions.
- C. Read the
RoleLastUsedfield returned byaws iam get-role. - D. Use IAM Access Analyzer policy generation on the role's CloudTrail activity to draft a policy.
- E. Run the policy simulator against the current policy to list the actions it allows.
Answers
- A. The credential report already holds password, MFA and key-usage columns for every user in one CSV. B shows permission usage, not credentials; C works but costs far more effort; D reports resources shared outside the zone of trust.
- B, D. Last accessed data shows which granted services and actions are used, and policy generation drafts a policy from real activity. A covers credentials, not permissions; C gives only the role's last use date and Region; E shows what is allowed, not what is used.
11. IAM Access Analyzer findings and policy generation
Concept
- IAM Access Analyzer reasons automatically over policies to answer three questions: who outside my zone of trust can reach this resource (external access), who inside my organization can reach my critical resources (internal access), and which granted access is never used (unused access). It also drafts policies from CloudTrail activity and validates policies (§6).
- The zone of trust is the account or the organization chosen when you create an analyzer; access by principals inside it is considered trusted.
- Findings are reviewed, archived when intended (archive rules automate that) or resolved by changing the policy, after which the analyzer rescans.
Exam facts
- External access analyzer: analyzes resource-based policies in its own Region only — create one in every Region you use. Supported resource types: S3 buckets and directory buckets, IAM roles (trust policies), KMS keys, Lambda functions and layers, SQS queues, Secrets Manager secrets, SNS topics, EBS snapshots, RDS DB and cluster snapshots, ECR repositories, EFS file systems, DynamoDB tables and streams.
- Findings distinguish public from cross-account access. A new or changed policy is analyzed within about 30 minutes; every policy is rescanned periodically (within 24 hours);
StartResourceScan(console Rescan) confirms a fix at once. - Internal access analysis (charged per monitored resource) shows which IAM users and roles inside the account or organization can reach selected critical resources (S3 buckets, DynamoDB tables and streams, RDS snapshots); like external access, it is Regional.
- Unused access analyzer (charged per IAM role and user analyzed per month): findings for unused roles, unused access keys, unused passwords and, for active identities, unused services and actions; "unused" means not used within the configured age of 1–365 days; its findings don't depend on Region, so one analyzer is enough; service-linked roles are not analyzed.
- Policy generation: for a user or role, from the events of a CloudTrail trail over a window of up to 90 days; needs a trail and a service role that trusts
access-analyzer.amazonaws.comand can read the trail (cloudtrail:GetTrail, last accessed APIs,s3:GetObjectands3:ListBucketon the log bucket); action-level detail for many services and service-level for the rest; no action-level data for data events (S3 object calls such asGetObject, DynamoDB item calls such asPutItem), so add those actions by hand;iam:PassRoleis never included; the console keeps a generated policy for 7 days. You still add resource ARNs and conditions, then test. - The template may be split into several policies when it exceeds the 6,144-character managed policy limit.
- Policy validation and custom policy checks belong to the same service but run on demand (§6).
- Analyzer types:
ACCOUNT,ORGANIZATION(external access),ACCOUNT_UNUSED_ACCESS,ORGANIZATION_UNUSED_ACCESS,ACCOUNT_INTERNAL_ACCESS,ORGANIZATION_INTERNAL_ACCESS; organization analyzers are created from the management account or a delegated administrator.
Developer angle
# external access, organization as zone of trust (run in every Region you use)
aws accessanalyzer create-analyzer --analyzer-name org-external --type ORGANIZATION
aws accessanalyzer list-findings-v2 \
--analyzer-arn arn:aws:access-analyzer:eu-west-1:111122223333:analyzer/org-external \
--filter '{"status": {"eq": ["ACTIVE"]}}'
aws accessanalyzer start-resource-scan \
--analyzer-arn arn:aws:access-analyzer:eu-west-1:111122223333:analyzer/org-external \
--resource-arn arn:aws:s3:::invoices-bucket
# unused access across the organization (paid, one analyzer is enough)
aws accessanalyzer create-analyzer --analyzer-name org-unused --type ORGANIZATION_UNUSED_ACCESS
# draft a least-privilege policy from 90 days of the role's CloudTrail activity
JOB_ID=$(aws accessanalyzer start-policy-generation \
--policy-generation-details principalArn=arn:aws:iam::111122223333:role/orders-fn-role \
--cloud-trail-details '{
"trails": [{"cloudTrailArn": "arn:aws:cloudtrail:eu-west-1:111122223333:trail/main", "allRegions": true}],
"accessRole": "arn:aws:iam::111122223333:role/service-role/AccessAnalyzerMonitorServiceRole",
"startTime": "2026-07-01T00:00:00Z",
"endTime": "2026-09-29T00:00:00Z"
}' --query jobId --output text)
aws accessanalyzer get-generated-policy --job-id "$JOB_ID" # once the job has succeeded
- Typical workflow for a broad role: generate a policy from activity → add ARNs and conditions →
validate-policyandcheck-no-new-access(§6) → attach it and remove the broad policy → watch unused access findings.
Traps
- Trap: "One external access analyzer covers every Region." → Reality: external (and internal) access analysis is Regional; unused access is not.
- Trap: "The external access analyzer reports unused permissions." → Reality: that is the separate, paid unused access analyzer.
- Trap: "The generated policy includes
iam:PassRoleand S3 object reads." → Reality: PassRole and data-event actions (S3 object or DynamoDB item calls) are never included; add them by hand if needed. - Trap: "A generated policy can go straight to production." → Reality: add resources and conditions, validate and test first.
- Trap: "Access Analyzer evaluates identity-based policies to find external sharing." → Reality: external access comes from resource-based policies, including role trust policies.
- Trap: "A fixed bucket policy clears the finding instantly." → Reality: analysis takes up to about 30 minutes; rescan to confirm sooner.
Practice questions
-
A company wants continuous alerts whenever an S3 bucket, a KMS key, an IAM role trust policy or an SQS queue anywhere in its organization grants access to principals outside the organization, with the LEAST operational overhead. What should a developer configure?
- A. A scheduled Lambda function that parses every resource policy and compares the account IDs it finds.
- B. A weekly export of the IAM credential report to the security team's mailbox.
- C. An IAM Access Analyzer external access analyzer with the organization as zone of trust, in each Region in use.
- D. An IAM Access Analyzer unused access analyzer for the organization.
-
A developer plans to use IAM Access Analyzer policy generation to replace a broad policy on a role. Which TWO statements are correct? (Select TWO.)
- A. The generated policy automatically includes
iam:PassRolefor the roles the application passes. - B. The generated policy lists S3 data-event actions such as
s3:GetObjectat action level. - C. A CloudTrail trail must be logging the account's activity, and up to 90 days of events can be analyzed.
- D. An unused access analyzer must exist in the Region before a policy can be generated.
- E. Resource ARNs and conditions still have to be added and tested before the policy goes to production.
- A. The generated policy automatically includes
Answers
- C. External access analyzers continuously evaluate resource-based policies against the zone of trust; one is needed per Region. A recreates the feature with custom code; B is about credentials, not sharing; D finds unused access, not external access.
- C, E. Generation reads up to 90 days from a trail and produces a template that still needs resources and conditions. A and B are false because PassRole and data events are never included; D is false because policy generation doesn't depend on an unused access analyzer.
12. Best practices and shared responsibility
Concept
- Shared responsibility for IAM: AWS secures the cloud — the infrastructure that runs IAM, its availability and global replication, the physical data centres. You secure what you configure in it — identities, credentials, policies, MFA, and the reviews that keep permissions small.
- AWS's IAM best-practice list now centres on two ideas: temporary credentials for people and workloads, and least privilege that you keep refining with Access Analyzer and last accessed data.
- Several course-era guidelines have been reworded; the logic (no root, MFA, least privilege, audit) is unchanged, but the default tools changed from IAM users and access keys to federation and roles.
Exam facts
| Area | AWS (security of the cloud) | You (security in the cloud) |
|---|---|---|
| IAM service | Infrastructure, availability, replication of IAM data across Regions | Which users, groups, roles and policies exist |
| Credentials | Issuing and rotating temporary credentials it hands to roles | Password policy, MFA for IAM users, access key lifecycle |
| Permissions | Evaluating policies exactly as written | Least privilege, conditions, guardrails, removing unused access |
| Monitoring | Recording events in CloudTrail and providing the analysis tools | Turning on trails, analyzers and alerts, and acting on findings |
- Current AWS IAM best practices (14, condensed):
- Human users federate with an identity provider (IAM Identity Center) and use temporary credentials.
- Workloads use IAM roles: AWS delivers role credentials to EC2 and Lambda; outside AWS use IAM Roles Anywhere,
AssumeRoleWithSAML/AssumeRoleWithWebIdentity, IoT Core, ECS Anywhere, EKS Hybrid Nodes or Systems Manager hybrid activations. - Require MFA, preferably phishing-resistant passkeys or security keys.
- Update access keys when needed for use cases that require long-term credentials, guided by last-used data.
- Protect root user credentials (§1).
- Apply least-privilege permissions.
- Start with AWS managed policies, then move to customer managed policies that fit your use case.
- Generate least-privilege policies from activity with IAM Access Analyzer (§11).
- Regularly review and remove unused users, roles, permissions, policies and credentials (§10).
- Use conditions to restrict access further (§5).
- Verify public and cross-account access with IAM Access Analyzer.
- Validate policies with IAM Access Analyzer (§6).
- Set permissions guardrails across accounts with SCPs (principals) and RCPs (resources); neither grants permissions.
- Delegate permissions management within an account with permissions boundaries, which also grant nothing by themselves.
- ⚠️ Changed: prefer temporary credentials and update long-term access keys when needed (for example when an employee leaves), guided by last-used data. Older material may say to rotate every access key frequently on a fixed schedule.
- ⚠️ Changed: AWS Single Sign-On was renamed AWS IAM Identity Center on July 26, 2022; the CLI command
aws configure ssoand thesso,sso-admin,sso-oidcandidentitystoreAPI namespaces kept their names. Older material may say "AWS SSO". - Course-era guidance mapped to today: a dedicated IAM user for every person → federate people; access keys as the way into the CLI and SDKs → temporary credentials (§9); permissions attached through groups → still right for the IAM users you keep; the credential report plus Access Advisor as the audit toolkit → add last accessed data, Access Analyzer and CloudTrail.
Developer angle
- Local development without keys (cli-sdk-policies guide):
aws configure ssofor Identity Center users,aws loginfor console credentials; code then builds clients without credentials:
import boto3
# No keys in code: the SDK finds credentials from the environment, an SSO or login
# session, a container role or the instance profile (cli-sdk-policies guide).
s3 = boto3.client("s3")
s3.put_object(Bucket="reports-bucket", Key="2026/oct.csv", Body=b"id,total\n1,42\n")
- A periodic hygiene job: credential report (password without MFA, old or unused keys) → last accessed data or unused access findings (permissions nobody uses) →
RoleLastUsed(dead roles) → fix or delete →validate-policyandcheck-no-new-accessfor every policy change in CI. - Guardrails belong in SCPs and RCPs and in-account delegation in permissions boundaries (advanced-identity guide); none of them grant permissions, so an allow must still exist in an identity-based or resource-based policy.
Traps
- Trap: "Under shared responsibility, AWS rotates your IAM users' access keys." → Reality: the key lifecycle is yours; AWS rotates only the temporary credentials it issues to roles.
- Trap: "AWS turns on MFA for your IAM users." → Reality: root MFA is enforced; IAM user MFA is yours to register and require.
- Trap: "An SCP that allows S3 gives the account's developers S3 access." → Reality: SCPs and boundaries only limit; an identity-based or resource-based allow is still required.
- Trap: "IAM Identity Center and AWS SSO are two products to compare." → Reality: same service, renamed in 2022.
- Trap: "Best practice is one IAM user with keys per developer." → Reality: federate developers and use temporary credentials.
- Trap: "Start every new role with
AdministratorAccessand trim later, it is AWS guidance." → Reality: start from AWS managed policies that match the job and move toward least privilege.
Practice questions
-
A startup has 40 developers working across three AWS accounts. Each developer has an IAM user with long-term access keys for the CLI. Which change is MOST secure while keeping operational overhead low?
- A. Keep the IAM users and rotate every access key each 30 days through a scheduled Lambda function that emails the new keys.
- B. Store each developer's access keys in AWS Secrets Manager and fetch them in the developers' shell scripts.
- C. Replace the individual IAM users with one shared IAM user per team that requires MFA at console sign-in.
- D. Federate the developers through IAM Identity Center and have them use
aws configure ssofor CLI access.
-
During a security review, a developer must list which IAM-related tasks are the company's responsibility under the AWS shared responsibility model. Which TWO belong to the company? (Select TWO.)
- A. Requiring MFA for IAM users and enforcing it in policies.
- B. Patching the servers that run the IAM service.
- C. Replicating IAM data across AWS data centres and Regions.
- D. Reviewing last accessed information and removing unused permissions.
- E. Securing the physical facilities that host the IAM infrastructure.
Answers
- D. Identity Center gives every developer temporary, MFA-backed credentials across the accounts with central permission sets. A keeps long-term keys and adds custom code; B still distributes long-term keys; C removes individual accountability and keeps shared long-term credentials.
- A, D. Configuring MFA and keeping permissions minimal are security in the cloud. B, C and E are AWS's side: infrastructure, replication and physical security.
Quick decision rules
Root user (§1)
- If you see a root-only task (account email, closing a standalone account, S3 MFA Delete, a bucket or queue policy that denies everyone), think root credentials — or
sts:AssumeRootwith a task policy in an Organizations member account. - If you see "remove root credentials from all member accounts at scale", think centralized root access management for AWS Organizations.
- If you see a script that needs root programmatically, think
aws loginwith root credentials, never root access keys.
Users, groups and roles (§2–§3)
- If you see many IAM users needing the same permissions, think one user group with a customer managed policy.
- If you see a group named as
Principal, or a group inside a group, think invalid: a group is neither a principal nor a container of groups; it just attaches identity policies to its users. - If you see
DeleteConflictfromDeleteUser, think attached keys, MFA devices, policies, groups or certificates must go first. - If you see code on EC2, Lambda, ECS or CodeBuild 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 (
Principalservice +sts:AssumeRole), not permissions. - If you see a role under
/aws-service-role/that you want to edit, think service-linked role: defined by the service, read-only to you.
Policies and conditions (§4–§5)
- If you see
Principalin an identity-based policy, thinkMalformedPolicyDocument: remove it. - If you see
Describe*orList*scoped to a specific ARN that "doesn't work", think no resource-level permissions: use"Resource": "*". - If you see
s3:GetObjectallowed on the bucket ARN only, think addbucket/*. - If you see
AllowwithNotAction, think over-granting, including future services. - If you see policy variables treated literally, think missing or old
Version(must be2012-10-17). - If you see "deny unless MFA, including access-key calls", think
BoolIfExists+aws:MultiFactorAuthPresent=false. - If you see
aws:SourceIpfailing for VPC endpoint traffic or calls made by CloudFormation, think the key is absent or shows the service's address. - If you see a Region-restriction policy that breaks IAM, think IAM resolves to us-east-1: exempt global services with
NotAction. - If you see a sixth managed policy version rejected with
LimitExceeded, think delete a non-default version (5 maximum).
Testing and troubleshooting (§6)
- If you see "with an explicit deny in a <type> policy", think change that deny; no allow can win.
- If you see "because no <type> policy allows", think add an allow in that policy type.
- If you see "test a policy without making real calls", think policy simulator (cli-sdk-policies guide); "lint a policy", think
validate-policy; "block broader access in CI", thinkcheck-no-new-access; "never public", thinkcheck-no-public-access. - If you see a role or policy that works only minutes after creation, think eventual consistency: create IAM resources in an earlier step, wait, and retry with backoff.
- If you see a password policy that "lost" its other settings after one CLI change, think
update-account-password-policyhas no partial updates. - If you see a passkey user needing CLI credentials, think
aws login(FIDO MFA can't be passed to STS). - If you see access key rotation without downtime, think create the second key, switch, verify last use, deactivate, delete.
- If you see a leaked
AKIA…key ID, thinksts get-access-key-infofor the account, then find the user, deactivate and replace. - If you see people or local development 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 (once every 4 hours).
- If you see "which permissions are unused", think last accessed information or the Access Analyzer unused access analyzer.
- If you see "who outside the organization can reach this bucket, key or role", think Access Analyzer external access, one analyzer per Region.
- If you see "write a least-privilege policy from what the role actually did", think Access Analyzer policy generation (CloudTrail, up to 90 days).
- If you see an SCP, RCP or permissions boundary expected to grant access, think guardrail only: an allow must exist elsewhere.
Changed vs older material
Learn the Current column; older practice material may still show the Older material may say value. Once you confirm them, the same rows go to notes/current-facts.md with their verification dates.
| Topic | Older material may say | Current (verified 2026-10-12) | Source |
|---|---|---|---|
| §1 · Root user MFA | Root MFA is a recommended best practice but optional | Required for the root user of every account type (standalone, management, member); a root user without MFA must register one within 35 days of its first console sign-in attempt | AWS docs |
| §1 · Root credentials of Organizations member accounts | Every member account has root credentials that must each be secured with MFA | Centralized root access (since Nov 15, 2024): delete member root password, keys, certificates and MFA; privileged tasks via sts:AssumeRoot (Regional endpoint, ≤ 900 s, 5 task policies) |
What's New |
| §1 · Tasks that require root | Only root can change the account name or the Support plan | Standalone accounts need root only for the email address, root password and root access keys (plus closing and reopening the account); name, contacts, alternate contacts, payment currency and Regions don't | AWS docs |
| §2 · Identities for people | A dedicated IAM user for every person | Human users federate (IAM Identity Center or an IdP) with temporary credentials; IAM users only for workloads that can't use roles, third-party tools, CodeCommit, Keyspaces and emergency access | AWS docs |
| §3 · Managed policies per role | 10 by default, adjustable to 20 | 20 by default, adjustable up to 25 | AWS docs |
| §3–§4 · Adjustable IAM quota maximums | Roles up to 5,000; customer managed policies up to 5,000; instance profiles up to 5,000; role trust policy up to 4,096 characters | Roles up to 10,000; customer managed policies up to 10,000; instance profiles up to 10,000; role trust policy up to 8,192 characters (defaults unchanged: 1,000 / 1,500 / 1,000 / 2,048) | AWS docs |
| §8 · MFA devices per user | One MFA device per IAM user or root user | Up to 8 devices of any type per user (since Nov 16, 2022); passkeys supported (since June 11, 2024) and recommended | What's New |
| §8 · MFA device types and vendors | U2F security keys, Gemalto key fobs, SurePassID fobs for GovCloud | Passkeys and FIDO security keys, virtual authenticator apps, hardware TOTP tokens from Thales (OTP token or display card) or, for GovCloud, Hypersecu | AWS |
| §8 · SMS MFA | SMS text messages are an MFA option | AWS ended support for enabling SMS MFA | AWS docs |
| §9 · Programmatic access | Create access keys for CLI and SDK use | Long-term keys are "not recommended"; use aws login, IAM Identity Center, CloudShell, roles or IAM Roles Anywhere |
AWS docs |
| §10 · Access Advisor | Access Advisor tab, service-level data for users (action detail for S3 only) | Last accessed information on the Last Accessed tab for users, groups, roles and policies; action-level for S3 (2020), EC2, IAM and Lambda (2021) and other services' management actions (since May 23, 2023) | AWS docs |
| §12 · Access key guidance | Rotate all access keys regularly | Prefer temporary credentials; update long-term keys when needed, guided by last-used data | AWS docs |
| §12 · AWS SSO | AWS Single Sign-On (AWS SSO) | Renamed AWS IAM Identity Center on July 26, 2022; aws configure sso and the sso* API namespaces unchanged |
AWS docs |