IAM Day 6 Lecture
SCS-C03 · Domain 4 · Identity and Access Management · Day 6 of 6

Troubleshoot access and prove least privilege

The last IAM day. When access fails or is too wide, how do you find out why and prove the fix? Today: reading AccessDenied messages, CloudTrail evidence, the IAM policy simulator, IAM Access Analyzer (external, internal and unused access, policy validation, custom checks) and last accessed information. We finish with a review quiz across all six days.

Skill 4.1.3 troubleshoot with CloudTrailSkill 4.2.5 policy simulator, Access Analyzer~80 min read + 30 min lab
01

Reading an AccessDenied message

Troubleshooting starts with the error itself. Most AWS services now return an enhanced access denied message that names who was denied, what action, on which resource, and which type of policy caused it. That last part tells you whether to hunt for an explicit Deny or add a missing Allow, and exactly where (SCP, RCP, boundary, session policy, resource policy, VPC endpoint policy, trust policy or identity policy).

Concept profile

Enhanced AccessDenied message

often confused with encoded authorization message
?What is it?

A plain-text error of the form User X is not authorized to perform Y on resource Z because …, including the policy type, and the policy ARN when available.

⚙How does it work?
  • “with an explicit deny in a type policy” → a Deny statement matched; find and narrow it.
  • “because no type policy allows the action” → nothing in that layer allowed it; add an Allow there.
  • If the message includes a policy ARN, check that policy; if not, check all policies of that type.
  • Some services (notably EC2) return an encoded message instead; decode it with aws sts decode-authorization-message (needs sts:DecodeAuthorizationMessage).
{ }Structure
WHOUser: arn:aws:sts::111122223333:assumed-role/Dev/minhWHATis not authorized to perform: s3:GetObjectWHEREon resource: arn:aws:s3:::payroll/2026.csvWHYwith an explicit deny in a service control policy: p-abc123“explicit deny in a …” → find the Deny. “because no … policy allows” → add or fix an Allow.
Four parts: who, what, where, why.
⇄Enhanced AccessDenied message vs encoded authorization message
Enhanced messageEncoded authorization message
Looks likeReadable sentence with policy typeLong encoded blob
Typical serviceS3, IAM, most servicesEC2 (Client.UnauthorizedOperation)
How to readJust read itsts:DecodeAuthorizationMessage
ShowsPolicy type (+ ARN sometimes)allowed, explicitDeny, principal, action, resource, condition values
!Common mistakes the exam tests
  • Adding more Allow statements when the message says “explicit deny”. An Allow never beats a Deny.
  • Editing the identity policy when the message says “no resource-based policy allows” (cross-account) or “no permissions boundary allows”.
  • Forgetting the trust policy: “not authorized to perform sts:AssumeRole” is about the role's trust policy (or the caller's policy).
  • Thinking every service gives the full message. Some still don't; fall back to CloudTrail.
✓Benefits and use cases
  • Fix the right layer first time.
  • Faster incident response.
  • Ordering questions on the exam often start with “read the error message”.
Think of it like · a parking ticket

A good ticket tells you exactly which rule you broke, so you know what to fix.

AWS conceptFamiliar thing
WHO (principal ARN)≈The car's number plate
WHAT (action)≈“Parked”
WHERE (resource ARN)≈“In front of gate 4”
“explicit deny in a SCP”≈“City law: no parking here, ever”: no permit can override it
“no identity policy allows”≈“You have no parking permit”: go get one
Encoded message≈A ticket written in code; take it to the office (sts) to decode
Real AWS example · decoding an EC2 error

EC2 returns an encoded message. The decoded JSON shows whether an explicit Deny matched and the condition values.

$ aws ec2 stop-instances --instance-ids i-0abc123
An error occurred (UnauthorizedOperation) ... Encoded authorization failure message: 4Gk2...Q

$ aws sts decode-authorization-message --encoded-message 4Gk2...Q \
    --query DecodedMessage --output text | jq .
{ "allowed": false,
  "explicitDeny": true,
  "matchedStatements": { "items": [ { "statementId": "DenyProdStop" } ] },
  "context": { "principal": { "arn": "arn:aws:sts::111122223333:assumed-role/Dev/minh" },
               "action": "ec2:StopInstances",
               "resource": "arn:aws:ec2:eu-west-1:111122223333:instance/i-0abc123",
               "conditions": { "items": [ { "key": "aws:ResourceTag/env", "values": ["prod"] } ] } } }

The decoded message may reveal policy details, so only give sts:DecodeAuthorizationMessage to people who troubleshoot.

Exam-style scenario

A developer gets: User: arn:aws:sts::111122223333:assumed-role/Dev/ana is not authorized to perform: dynamodb:PutItem on resource: arn:aws:dynamodb:eu-west-1:111122223333:table/Orders because no permissions boundary allows the dynamodb:PutItem action. The Dev role's inline policy allows dynamodb:*. What should the engineer change?

  1. A. Add another Allow for dynamodb:PutItem to the inline policy.
  2. B. Add dynamodb:PutItem to the permissions boundary attached to the Dev role (if it is meant to be allowed).
  3. C. Remove the SCP.
  4. D. Add a resource-based policy to the table.
Show the worked answer

Answer: B

The message names the layer: the boundary has no Allow. Effective permissions are the intersection of identity policy and boundary, so the boundary must also allow it.

Why not the others

  • A: The identity policy already allows it; more Allows there don't help.
  • C: The message doesn't mention an SCP.
  • D: Same-account access through a role is still capped by the boundary.
How the exam tests this
  • ““explicit deny in a service control policy””→ Fix or exempt in the SCP; Allows elsewhere won't help.
  • ““no identity-based policy allows””→ Add an Allow to the user/role.
  • “EC2 returns an encoded message”→ sts:DecodeAuthorizationMessage.
  • ““not authorized to perform sts:AssumeRole””→ Check the trust policy and caller's permission.
Must remember
  • Explicit deny → find the Deny. No … allows → add an Allow in that layer.
  • Message names the policy type (+ ARN when available).
  • EC2: decode-authorization-message.
  • Types: SCP, RCP, VPC endpoint, boundary, session, resource, trust, identity.
02

Investigating with CloudTrail

When the user can't give you the error, or you need to know who did something, CloudTrail is the evidence. Every API call, allowed or denied, becomes an event with userIdentity, eventName, sourceIPAddress, errorCode and errorMessage. The catch: data events (S3 object reads, Lambda invokes, DynamoDB item calls) are not recorded unless you turn them on.

Concept profile

Data events

often confused with management events
?What is it?

CloudTrail records of operations on or in a resource (the data plane): S3 object-level APIs, Lambda Invoke, DynamoDB item APIs and more. High volume, so off by default and charged.

⚙How does it work?
  • Create a trail or CloudTrail Lake event data store.
  • Add data event selectors (advanced event selectors can filter by resource ARN, event name, read/write).
  • Events land in S3 / CloudWatch Logs / Lake.
  • Query them: Athena over the S3 logs or SQL in CloudTrail Lake.
{ }Structure
Management events control plane · logged by default iam:AttachRolePolicyec2:RunInstancess3:PutBucketPolicysts:AssumeRole Event history: last 90 days, free Data events data plane · OFF unless you enable them s3:GetObject / PutObjectlambda:Invokedynamodb:GetItems3:DeleteObject Needs a trail or event data store Which APIs count as data events differs per service; check the docs.
If the question is about object reads and nobody enabled data events, there is no record.
⇄Data events vs management events
Data eventsManagement events
PlaneData plane (inside a resource)Control plane (configure resources)
DefaultOffOn (Event history 90 days)
CostCharged per eventFirst copy of management events free in a trail
Exampless3:GetObject, lambda:InvokeCreateUser, PutBucketPolicy, AssumeRole
In Event historyNoYes
!Common mistakes the exam tests
  • Looking in Event history for S3 GetObject denials: Event history has management events only.
  • Assuming CloudTrail is off by default: Event history is always on for management events (90 days); a trail is needed for longer retention and data events.
  • Ignoring the sessionContext: it shows which role the session came from and whether MFA was used.
  • Forgetting multi-Region and organization trails when an action happened in another Region or account.
✓Benefits and use cases
  • Prove who changed a policy and when.
  • Find every AccessDenied in the last week for a role.
  • Long-term evidence for audits (organization trail to a locked-down log archive account).
Think of it like · a building's security cameras

Cameras at the doors vs cameras inside every room.

AWS conceptFamiliar thing
Management events≈Cameras at the main doors and the manager's office: always on
Data events≈Cameras inside every storeroom: costly, only where you install them
Event history≈The last 90 days of door footage, kept for free
Trail / Lake≈The long-term archive you pay for
userIdentity≈The name badge visible on the footage
Real AWS example · CloudTrail Lake: all AccessDenied for one role in the last 7 days

CloudTrail Lake lets you run SQL over events in an event data store.

Availability (checked October 2026): CloudTrail Lake closed to new customers on 31 May 2026. Existing customers can keep using it. For new setups AWS recommends Amazon CloudWatch (and offers migration of Lake data there); Athena over the trail’s S3 logs also works. Exam questions may still name CloudTrail Lake as the answer for “SQL over CloudTrail events without managing tables”.
SELECT eventTime, eventSource, eventName, errorCode, errorMessage, sourceIPAddress
FROM 8f2e1c4a-EXAMPLE-EVENT-DATA-STORE
WHERE userIdentity.sessionContext.sessionIssuer.arn
      = 'arn:aws:iam::111122223333:role/Dev'
  AND errorCode IN ('AccessDenied', 'Client.UnauthorizedOperation')
  AND eventTime > date_add('day', -7, now())
ORDER BY eventTime DESC

sessionIssuer.arn is the role (with its path); the session ARN alone doesn't include the path (Day 5).

Exam-style scenario

After a data leak, investigators need to know which IAM principal downloaded objects from the finance-exports bucket last month. The account has a multi-Region trail with default settings. What will they find, and what should be changed for the future?

  1. A. The trail has every GetObject call; nothing to change.
  2. B. The trail has bucket configuration changes but no GetObject calls; enable S3 data events (at least for sensitive buckets) and consider S3 server access logs.
  3. C. Use IAM last accessed information to see which objects were read.
  4. D. Event history keeps GetObject calls for 90 days.
Show the worked answer

Answer: B

Default trails log management events only. Object-level reads are data events and must be enabled explicitly.

Why not the others

  • A: Data events are off by default.
  • C: Last accessed is service/action level, never per object.
  • D: Event history has management events only.
How the exam tests this
  • “Who read S3 objects?”→ Data events (must be enabled) or S3 server access logs.
  • “Who changed this policy?”→ Management event, e.g. PutRolePolicy, userIdentity.
  • “SQL over events without Athena setup”→ CloudTrail Lake.
  • “Which role did this session come from?”→ sessionContext.sessionIssuer.
Must remember
  • Event history: management events, 90 days.
  • Data events: off by default, charged.
  • errorCode AccessDenied / Client.UnauthorizedOperation.
  • Org trail → log archive account for evidence.
03

IAM policy simulator

The IAM policy simulator answers “would this principal be allowed to do this?” without actually doing it. You pick a user, group or role (or paste draft policies), choose actions and resources, add context keys, and it shows allowed, explicitly denied or implicitly denied, and which statement decided. It's the safe way to reproduce a problem or test a change before deploying it.

Concept profile

IAM policy simulator

often confused with IAM Access Analyzer policy checks
?What is it?

A console tool and API (SimulatePrincipalPolicy, SimulateCustomPolicy) that evaluates policies against simulated requests. It makes no real AWS calls.

⚙How does it work?
  • Choose the principal (its identity policies and boundary are loaded) or paste policies.
  • Choose actions, resources and condition key values (e.g. aws:SourceIp, aws:MultiFactorAuthPresent).
  • Optionally include a resource-based policy (the console can fetch some; the API needs you to supply it).
  • Read the result and the matching statement for each action.
{ }Structure
IncludedNot included / may differ
Identity-based policies (attached or draft)Resource control policies (RCPs): not supported
Permissions boundariesReal context values from production
SCPs (account in an organization)VPC endpoint policies, role chaining, multiple resource policies can differ
Resource-based policies you supplyDoesn't check whether a service supports a global condition key
⇄IAM policy simulator vs IAM Access Analyzer policy checks
Policy simulatorAccess Analyzer checks
Question“Is THIS request allowed?”“Is this policy valid / does it grant new, public or forbidden access?”
InputPrincipal + action + resource + contextPolicy document(s)
MethodSimulated evaluationGrammar checks + automated reasoning
Best forReproducing one denial, testing a changeCI/CD gates, finding risky statements
Real calls?NoNo
!Common mistakes the exam tests
  • Trusting a simulator “allowed” when an RCP or VPC endpoint policy might deny in real life.
  • Forgetting to supply the resource-based policy in API simulations.
  • Not setting context keys: a condition on MFA or IP looks denied (or allowed) only because the value is missing.
  • Thinking the simulator changes anything. It's read-only.
✓Benefits and use cases
  • Reproduce a user's AccessDenied safely.
  • Test a new SCP or boundary before attaching it.
  • Automate permission regression tests with the API.
Think of it like · a flight simulator

Pilots practise the dangerous move without risking a real plane.

AWS conceptFamiliar thing
Policy simulator≈The flight simulator
Simulated request≈A practice landing
Context keys≈Weather settings you type in: wind, fog (IP, MFA)
RCPs not supported≈A simulator that doesn't model one type of storm
Real AWS call≈The real flight: always confirm after practising
Real AWS example · simulate with the CLI, including MFA context

Does the Admin role's policy allow terminating instances without MFA?

aws iam simulate-principal-policy \
  --policy-source-arn arn:aws:iam::111122223333:role/Admin \
  --action-names ec2:TerminateInstances \
  --context-entries "ContextKeyName=aws:MultiFactorAuthPresent,ContextKeyValues=false,ContextKeyType=boolean"

# Result (shortened)
"EvalActionName": "ec2:TerminateInstances",
"EvalDecision": "explicitDeny",
"MatchedStatements": [{ "SourcePolicyId": "RequireMFA" }]

Run it again with true to confirm the role works when MFA is present.

Exam-style scenario

A security engineer drafts a new SCP and wants to confirm that the Dev role in a member account will still be able to call s3:PutObject but not iam:CreateUser, without risking production. What should they do FIRST?

  1. A. Attach the SCP to the production OU and watch CloudTrail.
  2. B. Use the IAM policy simulator for the Dev role (with SCPs in scope) for both actions, then attach the SCP to a test OU and verify.
  3. C. Run Access Analyzer external access analysis.
  4. D. Use IAM last accessed information.
Show the worked answer

Answer: B

The simulator evaluates identity policies, boundaries and SCPs without making real calls. Then confirm in a test OU, because the simulator can differ from live behaviour.

Why not the others

  • A: Risky: tests in production.
  • C: Shows external sharing, not whether an action is allowed.
  • D: Shows past usage, not future decisions.
How the exam tests this
  • “Test whether a principal can perform an action without doing it”→ IAM policy simulator.
  • “Simulator says allowed, reality says denied”→ Check RCPs, VPC endpoint policies, real context.
  • “Test a draft policy in automation”→ SimulateCustomPolicy API.
Must remember
  • Simulator: identity, boundary, SCP, supplied resource policy.
  • No RCPs; results can differ from live.
  • Set context keys (MFA, IP, tags).
  • No real calls: safe to test.
04

IAM Access Analyzer: external, internal and unused access

IAM Access Analyzer uses automated reasoning to read resource policies and tell you who can reach your resources. You pick a zone of trust (an account or the whole organization). An external access analyzer reports resources that principals outside the zone (or the public) can access. An internal access analyzer shows which principals inside the zone can reach critical resources you choose. An unused access analyzer reports roles, keys, passwords and permissions nobody uses.

Concept profile

External access analyzer

often confused with unused access analyzer
?What is it?

Continuously analyzes resource-based policies of supported resources in one Region and creates a finding for each resource shared outside the zone of trust.

⚙How does it work?
  • Create an analyzer with zone of trust = account or organization (organization needs the management account or a delegated administrator).
  • It analyzes supported resources: S3 buckets, IAM role trust policies, KMS keys, Lambda, SQS, SNS, Secrets Manager, EBS/RDS snapshots, ECR, EFS, DynamoDB and more.
  • Each external or public access becomes an active finding.
  • Fix the policy (finding becomes resolved) or, if intended, archive it, ideally with an archive rule.
{ }Structure
Zone of trust: your organization (or account) Account Brole reads "reports" Account AS3 bucket "reports" Access inside the zone = trusted, no external finding (internal access analyzer can still watch critical resources inside the zone) Vendor accountoutside the org Active findingexternal / public access
Only access crossing the zone of trust becomes an external finding.
⇄External access analyzer vs unused access analyzer
External accessUnused access
FindsResources reachable from outside the zone (or public)Unused roles, access keys, passwords, permissions
ReadsResource-based policiesIdentity policies + activity
ScopePer Region: create in each RegionNot Region-specific
CostNo extra chargePaid (per role/user analyzed)
ActionRestrict policy or archiveRemove/right-size; use policy recommendations
!Common mistakes the exam tests
  • Creating an external analyzer in one Region and assuming it covers all Regions.
  • Using zone of trust = account in a multi-account org, then drowning in findings for normal cross-account access within the org.
  • Archiving findings by hand every time instead of an archive rule for known partners.
  • Expecting external access analysis to report unused permissions (that's the unused access analyzer).
✓Benefits and use cases
  • Detect accidental public buckets or cross-account KMS keys.
  • Continuous least-privilege clean-up of unused roles and permissions.
  • Findings go to EventBridge and Security Hub for automation.
Think of it like · a gated community

The guard cares about visitors from outside the gate.

AWS conceptFamiliar thing
Zone of trust≈The gated community
External finding≈A house whose back door is open to the street
Internal access analyzer≈Checking which neighbours have keys to the bank vault inside
Unused access finding≈Spare keys nobody has used for a year
Archive rule≈“Deliveries from our usual courier are fine”
Per-Region analyzer≈One guard per gate; every gate needs its own
Real AWS example · archive rule for a trusted partner and an unused access finding

An archive rule stops repeat findings for a known partner account; an unused access analyzer with a 90-day window flags stale roles.

aws accessanalyzer create-archive-rule --analyzer-name org-external \
  --rule-name trusted-auditor \
  --filter '{"principal.AWS": {"eq": ["444455556666"]}, "isPublic": {"eq": ["false"]}}'

aws accessanalyzer create-analyzer --analyzer-name org-unused \
  --type ORGANIZATION_UNUSED_ACCESS \
  --configuration '{"unusedAccess": {"unusedAccessAge": 90}}'

# Example unused finding
"findingType": "UnusedIAMRole",  "resource": "arn:aws:iam::111122223333:role/old-batch" 

Archived findings come back as new findings if the policy later grants additional access.

Exam-style scenario

A company with 60 accounts wants to be told whenever any S3 bucket, KMS key or IAM role in any Region becomes accessible from outside the organization. Which setup is correct?

  1. A. One external access analyzer in us-east-1 with zone of trust = management account.
  2. B. An external access analyzer with zone of trust = organization, created from the delegated administrator account in every Region in use, sending findings to Security Hub/EventBridge.
  3. C. An unused access analyzer for the organization.
  4. D. Turn on CloudTrail data events.
Show the worked answer

Answer: B

Organization zone of trust ignores normal in-org sharing. External access analyzers are Regional, so one is needed per Region.

Why not the others

  • A: Only one Region, and account-level zone would flag in-org access.
  • C: Finds unused access, not external sharing.
  • D: Records calls; doesn't analyze who policies allow.
How the exam tests this
  • “Find resources shared outside the org”→ External access analyzer, zone = organization, every Region.
  • “Find unused roles / keys / permissions”→ Unused access analyzer (paid).
  • “Expected sharing keeps creating findings”→ Archive rule.
  • “Who inside can reach critical data?”→ Internal access analyzer.
Must remember
  • External analyzer = per Region; unused = not Regional.
  • Zone of trust: account or organization.
  • Finding states: active, archived, resolved.
  • Unused & internal access are paid.
05

Policy validation, custom policy checks and policy generation

The other half of Access Analyzer works on policy documents before they are deployed. Policy validation lints a policy like a spell-checker. Custom policy checks use automated reasoning to prove a policy is safe against your rules, which makes them perfect gates in a CI/CD pipeline. Policy generation (Day 2) writes a starting least-privilege policy from CloudTrail activity.

Concept profile

Custom policy checks

often confused with policy validation
?What is it?

Access Analyzer APIs that return PASS or FAIL for a policy against a security standard you define, using automated reasoning.

⚙How does it work?
  • CheckNoNewAccess: does the updated policy grant anything the reference (old or baseline) policy didn't?
  • CheckAccessNotGranted: does the policy allow any of these sensitive actions or resources (e.g. iam:PassRole, s3:DeleteBucket)?
  • CheckNoPublicAccess: could this resource policy grant public access for this resource type?
  • Run them in the pipeline (CLI, CloudFormation hooks, CDK); FAIL stops the deploy. Charged per check.
{ }Structure
Validation findingMeaningExample
Security warningOverly permissiveiam:PassRole with Resource: *
ErrorWon't workInvalid action or ARN format
WarningDoesn't follow best practiceCondition key not supported by the action
SuggestionCan be improvedEmpty array or redundant statement
⇄Custom policy checks vs policy validation
Custom policy checksPolicy validation
Question“Does it break MY rules?”“Is it written correctly and safely?”
OutputPASS / FAIL (+ reasons)Findings by severity
MethodAutomated reasoningGrammar + best-practice checks
CostPaid per checkNo extra charge
Typical placeCI/CD gateConsole editor, CI/CD lint
!Common mistakes the exam tests
  • Using the policy simulator in a pipeline to prove “no new access”; that's CheckNoNewAccess.
  • Expecting policy validation to know your company's forbidden actions; that's CheckAccessNotGranted.
  • Using policy generation output in production without review (it can't see data events or PassRole needs, and only looks back up to 90 days).
  • Confusing CheckNoPublicAccess (a policy document) with an external access finding (a deployed resource).
✓Benefits and use cases
  • Shift-left: block risky IAM changes before deploy.
  • Let developers write policies while security keeps guardrails.
  • Generate a least-privilege starting point from real usage.
Think of it like · checking an essay

Spelling, house rules, and a summary from your notes.

AWS conceptFamiliar thing
Policy validation≈Spell-checker and grammar checker
CheckAccessNotGranted≈The teacher's list of banned words
CheckNoNewAccess≈“Did this edit add anything that wasn't in the approved draft?”
CheckNoPublicAccess≈“Would this be published to the whole internet?”
Policy generation≈Writing the essay from your actual notes (CloudTrail)
Real AWS example · CI/CD gate with custom policy checks

A pipeline step fails the build if a pull request adds new access or grants dangerous actions.

aws accessanalyzer check-no-new-access \
  --new-policy-document file://role-policy.new.json \
  --existing-policy-document file://role-policy.main.json \
  --policy-type IDENTITY_POLICY
{ "result": "FAIL",
  "message": "The modified permissions grant new access compared to your existing policy.",
  "reasons": [{ "description": "New access in the statement with index: 2." }] }

aws accessanalyzer check-access-not-granted \
  --policy-document file://role-policy.new.json --policy-type IDENTITY_POLICY \
  --access '[{"actions": ["iam:PassRole", "kms:ScheduleKeyDeletion"]}]'
{ "result": "PASS" }

FAIL is not always wrong: it means a human must review because access grew.

Exam-style scenario

Developers deploy IAM policies through CloudFormation. Security wants the pipeline to automatically block any change that would let a role call kms:Decrypt on the payments key or make an S3 bucket public, while not blocking ordinary changes. What should they add?

  1. A. IAM policy simulator calls in the pipeline for every action in AWS.
  2. B. Access Analyzer custom policy checks: CheckAccessNotGranted for kms:Decrypt on the key ARN and CheckNoPublicAccess for bucket policies, failing the build on FAIL.
  3. C. An external access analyzer.
  4. D. IAM last accessed information.
Show the worked answer

Answer: B

Custom policy checks prove before deployment that specific access isn't granted and that resource policies aren't public.

Why not the others

  • A: Simulating every action is impractical and isn't a proof.
  • C: Detects after deployment, doesn't block.
  • D: Looks at past usage only.
How the exam tests this
  • “Block deploys that add permissions”→ CheckNoNewAccess.
  • “Ensure policy never grants specific sensitive actions”→ CheckAccessNotGranted.
  • “Block public resource policies before deploy”→ CheckNoPublicAccess.
  • “Lint policy for errors and security warnings”→ Policy validation.
Must remember
  • Validation: security warning, error, warning, suggestion.
  • Custom checks: NoNewAccess, AccessNotGranted, NoPublicAccess; paid.
  • Custom checks = before deploy; analyzers = after.
  • Policy generation: from CloudTrail, review before use.
06

Last accessed information and right-sizing

Least privilege isn't a one-time job. Last accessed information in IAM shows, for each user, group, role or policy, which services (and for many services which actions) were last used and when. From the management account you can see it for the whole organization, OU or account, which tells you which services your SCPs could safely block.

Concept profile

IAM last accessed information

often confused with unused access analyzer
?What is it?

Data in IAM (console, GenerateServiceLastAccessedDetails API, Organizations view) showing when an identity last used each allowed service or management action.

⚙How does it work?
  • IAM compares the services a policy allows with authentication activity.
  • Service-level data covers at least 400 days; action-level data exists for many services' management actions.
  • Recent activity shows up within about 4 hours.
  • Use it to remove unused services from policies, delete unused roles, or tighten SCPs.
{ }Structure
ShowsDoesn't show
Which services an identity used and whenWhich specific resources (buckets, objects) were used
Action-level for many services (management actions)Data-plane actions such as each S3 GetObject
Org / OU / account view for SCP planningWhether access is shared externally
⇄IAM last accessed information vs unused access analyzer
Last accessed infoUnused access analyzer
WhereIAM console / APIAccess Analyzer
OutputA table you readFindings you can track, route and automate
CoverageOne identity/policy at a time (or org view)Every role and user in the account/org continuously
ExtrasOrganizations view for SCPsPolicy recommendations, EventBridge, Security Hub
CostFreePaid
!Common mistakes the exam tests
  • Expecting last accessed to tell which S3 objects or buckets were used (it's service/action level).
  • Deleting a role unused for 30 days that runs a yearly job: check the window against business cycles.
  • Forgetting the Organizations view when deciding which services to block with SCPs.
✓Benefits and use cases
  • Remove unused services from policies.
  • Find and delete stale users, keys and roles.
  • Design deny-list SCPs from real usage.
Think of it like · a gym membership card

The gym can see which rooms you scanned into, not which machines you touched.

AWS conceptFamiliar thing
Last accessed (service level)≈“Entered the pool area: last Tuesday”
Action level≈“Booked a class: last week”
Not resource level≈It doesn't know which locker you used
Org view≈Head office sees which rooms no member uses, so it can close them (SCP)
Unused access analyzer≈An automatic monthly report of members who never come
Real AWS example · find unused services for a role

Generate the report, then read which allowed services were never used.

JOB=$(aws iam generate-service-last-accessed-details \
        --arn arn:aws:iam::111122223333:role/Analytics --query JobId --output text)
aws iam get-service-last-accessed-details --job-id $JOB \
  --query 'ServicesLastAccessed[?TotalAuthenticatedEntities==`0`].ServiceNamespace'
[ "dynamodb", "kinesis", "sqs" ]

Three allowed services were never used in the tracking window: candidates to remove from the role's policies.

Exam-style scenario

A security team wants to add an SCP that denies services nobody in the Sandbox OU uses, without breaking teams. What is the MOST efficient way to find those services?

  1. A. Ask every team by email.
  2. B. Use IAM last accessed information from the management account for the Sandbox OU to see which services have not been accessed.
  3. C. Enable CloudTrail data events and wait a year.
  4. D. Run an external access analyzer.
Show the worked answer

Answer: B

The Organizations view of last accessed information shows, per OU, which services allowed by SCPs were used and when.

Why not the others

  • A: Slow and unreliable.
  • C: Unnecessary; last accessed data already covers 400 days.
  • D: Finds external sharing, not service usage.
How the exam tests this
  • “Which services can I remove from this role?”→ Last accessed information.
  • “Which services can the SCP deny for an OU?”→ Last accessed, Organizations view.
  • “Continuous findings for unused roles and permissions”→ Unused access analyzer.
Must remember
  • Last accessed: service + action, not resource.
  • ≥ 400 days of service data.
  • Org view helps design SCPs.
  • Automated, continuous = unused access analyzer.
07

Cheatsheet extras: credential report and tracing a leaked access key

The cheat sheet introduces the IAM credential report and the “keys leaked on GitHub” drill. This lecture already covered last accessed information and CloudTrail; here is what the credential report adds, what it does not contain, and the quickest way to find who owns a leaked key.

Concept profile

IAM credential report

often confused with last accessed information (Access Advisor)
?What is it?

A CSV file for one account that lists every IAM user (and the root user as <root_account>) with the status of their credentials: passwords, MFA, access keys and signing certificates.

⚙How does it work?
  • Create it with aws iam generate-credential-report, then download with aws iam get-credential-report (base64 content). Permissions: iam:GenerateCredentialReport and iam:GetCredentialReport.
  • IAM builds a new report at most every four hours; a newer request returns the existing one.
  • Columns show if a password is enabled and when it was last used, changed and must next be rotated; whether MFA is active; and for each of the two access keys whether it is active, when it was rotated, and when, where and for which service it was last used.
  • It contains no access key IDs and no permissions. It is per account: in an organization you run it in each account.
{ }Structure
Leaked key IDAKIA… sts get-access-key-info→ owning account ID In that account:get-access-key-last-used → UserName Deactivatethen rotate IAM credential report (CSV, one account) user · arn · password_enabled · password_last_used · password_next_rotation · mfa_active · access_key_1_active · access_key_1_last_rotated · access_key_1_last_used_date / region / service · (key 2 …) No access key IDs · no permissions · regenerated at most every 4 hours
Top: finding the owner of a leaked key. Bottom: what the credential report shows.
⇄IAM credential report vs last accessed information (Access Advisor)
Credential reportLast accessed information
AnswersAre credentials old, unused or missing MFA?Which services (and some actions) did this identity or policy use, and when?
CoversIAM users and rootUsers, groups, roles, policies; also OUs/accounts in Organizations
FormatCSV downloadConsole tab or API (GenerateServiceLastAccessedDetails)
Good forAudits: MFA missing, keys older than 90 days, unused passwordsRemoving unused permissions (least privilege)
Exam hint“list all users and the status of their passwords, keys and MFA”“which permissions can we remove?”
!Common mistakes the exam tests
  • Expecting to search the report for an access key ID: it only has key status and dates.
  • Running it once in the management account and expecting all member accounts: it is per account.
  • Using it to see permission changes during an incident: use AWS Config history or CloudTrail.
  • Using it for roles: roles have no long-term credentials, so they aren’t listed.
✓Benefits and use cases
  • Quarterly credential audits (CIS checks: MFA on all console users, keys rotated within 90 days).
  • Spotting users who never signed in or keys never used, before deleting them.
  • Evidence for auditors, together with AWS Config and Security Hub controls.
Real AWS example · who owns this leaked key?

Run the first command anywhere; it only reveals the account. Then work inside that account.

$ aws sts get-access-key-info --access-key-id AKIAEXAMPLELEAKED01
{ "Account": "444455556666" }

# signed in to account 444455556666
$ aws iam get-access-key-last-used --access-key-id AKIAEXAMPLELEAKED01
{ "UserName": "ci-deployer",
  "AccessKeyLastUsed": { "LastUsedDate": "2026-10-19T22:41:00Z", "ServiceName": "ec2", "Region": "ap-southeast-1" } }

$ aws iam update-access-key --user-name ci-deployer --access-key-id AKIAEXAMPLELEAKED01 --status Inactive
$ aws cloudtrail lookup-events --lookup-attributes AttributeKey=AccessKeyId,AttributeValue=AKIAEXAMPLELEAKED01

A key used in a Region the team never uses is a strong sign of misuse. CloudTrail lookup by AccessKeyId shows what it did (90 days of management events in Event history).

Mock answer corrected. One of your mocks says “generate a credential report in each account and look up the key IDs”. The report has no key IDs, so it can’t map a key to a user. Use sts get-access-key-info for the account and iam get-access-key-last-used (which returns UserName) or iam list-access-keys in that account. Also remember: AWS Config keeps the history of a user’s attached policies, so “did this user’s permissions change during the incident?” is a Config question.
Think of it like · a school office

The office keeps different registers for different questions.

AWS conceptFamiliar thing
Credential report≈The register of every pupil’s locker key and ID card: issued, last used, expired
Last accessed information≈The library log: which rooms each pupil actually used this term
get-access-key-info≈The prefix on a lost key tag that tells you which school it belongs to
get-access-key-last-used≈That school’s office looking up whose locker the key opens
Config history≈The office file showing when a pupil’s permissions slip was changed
Exam-style scenario

A list of leaked access key IDs is found in a public repository. The company has 40 accounts in AWS Organizations. The security engineer must quickly find which IAM user owns each key so the keys can be disabled. What is the fastest correct approach?

  1. A. Generate a credential report in the management account and search it for the key IDs.
  2. B. Generate IAM Access Analyzer reports in every account.
  3. C. Run aws sts get-access-key-info for each key to get its account, then in that account run aws iam get-access-key-last-used (or list-access-keys) to get the user and deactivate the key.
  4. D. Generate a credential report in every account and search them for the key IDs.
Show the worked answer

Answer: C

The STS call maps each key to its account without needing access to it; the IAM call in that account returns the user name.

Why not the others

  • A: Per account only, and it has no key IDs.
  • B: Access Analyzer finds external, internal and unused access, not key ownership.
  • D: Reports have no key IDs, so the search finds nothing.
How the exam tests this
  • “Status of every user’s password, keys and MFA”→ Credential report.
  • “Which account does this AKIA key belong to?”→ sts get-access-key-info.
  • “Which user owns this key / when was it last used?”→ iam get-access-key-last-used in that account.
  • “Did the user’s IAM permissions change before the incident?”→ AWS Config configuration history (plus CloudTrail for who).
  • “Exposed key: assess impact”→ Deactivate, then CloudTrail (all Regions) + credential report last-used dates.
Must remember
  • Credential report: per account, CSV, every 4 h max.
  • It has no key IDs and no permissions.
  • Key → account: GetAccessKeyInfo; key → user: GetAccessKeyLastUsed.
  • Permission history: AWS Config.
  • Roles aren’t in the credential report.
08

Lecture summary map

Day 6 on one screen, then the whole IAM domain on one screen.

1. Mind map

① AccessDeniedExplicit deny → find DenyNo … allows → add AllowEC2: decode message② CloudTrailerrorCode, userIdentityData events OFF defaultLake = SQL③ Policy simulatorNo real callsSCP yes, RCP noSet context keys④ Access AnalyzerExternal: per RegionZone of trustUnused / internal: paid⑤ Policy checksValidation severitiesNoNewAccess in CI/CDAccessNotGranted⑥ Last accessedService + actionNot resource levelOrg view → SCPsIAM Day 6troubleshoot + prove
Tap a branch to jump back to that part of the lecture. Try redrawing this map from memory on paper.

2. The troubleshooting path

1Read the errorpolicy type + ARN2Find the eventCloudTrail errorCode3Reproducepolicy simulator4Fix itleast privilege5Prove itAccess Analyzer
Read → find → reproduce → fix → prove. Ordering questions follow this path.

3. Which tool answers which question?

Why was I denied?→Error message + CloudTrailpolicy type, ARN, errorCodeEC2 encoded message→sts:DecodeAuthorizationMessageexplicitDeny, contextWill this policy work?→IAM policy simulatorno real calls · no RCPsWho outside can reach it?→Access Analyzer externalzone of trust · per RegionWhat is never used?→Unused access analyzerroles, keys, permissionsIs this policy safe to ship?→Validation + custom checksCheckNoNewAccess in CI/CDWhich services were used?→Last accessed informationservice / action, 400 days
Cover the middle column and name the tool.

4. The whole IAM domain in six days

Day 1Building blocksusers, groups, roles, policy typesDay 2Policy evaluationDeny → RCP → SCP → resource → identity → boundary → sessionDay 3Temporary credentialsSTS, trust policies, ExternalId, presigned URLsDay 4Human identitiesIdentity Center, SAML, AD, MFA, CognitoDay 5Access modelsRBAC, ABAC, paths, Verified PermissionsDay 6Troubleshoot + proveCloudTrail, simulator, Access Analyzer
If you can explain each row for two minutes without notes, you're ready for Domain 4.

5. “If the question says… pick…”

Error says “explicit deny in a service control policy”Change or exempt in the SCP
Error says “no identity-based policy allows”Add an Allow to the user/role
EC2 returns an encoded failure messagests:DecodeAuthorizationMessage
Who downloaded S3 objects?CloudTrail data events (must be enabled)
SQL over CloudTrail without building AthenaCloudTrail Lake
Test a change without making real callsIAM policy simulator
Resources shared outside the organizationAccess Analyzer external, org zone, every Region
Expected partner access keeps creating findingsArchive rule
Unused roles, keys, permissions continuouslyUnused access analyzer
Block deploys that add permissionsCheckNoNewAccess
Never allow these sensitive actionsCheckAccessNotGranted
Which services to remove / block by SCPLast accessed information (org view)
Day 6 in one sentence

Read the error, find the evidence, test without touching production, then let Access Analyzer prove nothing more is open than you meant.

09

Must-remember cheat sheet

1. “explicit deny in a X” → find the Deny in X.
2. “no X policy allows” → add an Allow in X.
3. EC2 encoded error → sts:DecodeAuthorizationMessage.
4. Event history = management events, 90 days.
5. Data events: off by default, charged.
6. CloudTrail Lake = SQL on events.
7. Simulator: identity, boundary, SCP; not RCP.
8. Simulator makes no real calls; set context keys.
9. External analyzer: per Region; zone = account/org.
10. Findings: active · archived · resolved; archive rules.
11. Unused & internal access analyzers are paid.
12. Validation: security warning / error / warning / suggestion.
13. CheckNoNewAccess · CheckAccessNotGranted · CheckNoPublicAccess.
14. Last accessed: service/action, ≥ 400 days, not resources.
15. Org view of last accessed → design SCPs.
16. Order: read → find → simulate → fix → prove.
17. Credential report: per account, CSV, no key IDs, no permissions, max every 4 h.
18. Leaked key owner: sts get-access-key-info → account; iam get-access-key-last-used → user.
10

Core quiz

Check the basics first. Pick an answer, then press Check.

11

Hard quiz

Harder than most real exam questions on purpose, including an ordering and a matching question. Aim for 9 out of 12.

12

IAM review quiz (Days 1–6)

Two questions from each day. If you miss one, reopen that day's lecture and its “Must remember” boxes.

13

Practice test questions (from your mock exams)

These questions come from your own mock exams (the SCS-003 mock 1–9 files in your study folder), matched to this lecture. Wording was tidied and checked against current AWS behavior; where a mock answer was outdated or wrong, the explanation says so. The source is shown at the end of each explanation, like “(Mock 5 · Q12)”. Pick an answer, then press Check.

14

Hands-on lab (30 minutes)

  1. Attach a policy to a test role that denies s3:DeleteObject. Try the delete and read the access denied message: note the policy type.
  2. Run aws iam simulate-principal-policy for that role and action, then again with a context key such as aws:MultiFactorAuthPresent.
  3. Create an external access analyzer (zone = account). Add a bucket policy that grants another account read access and watch the finding appear; then create an archive rule for it.
  4. Run aws accessanalyzer validate-policy on a policy with iam:PassRole on * and read the security warning.
  5. Run check-no-new-access comparing an old and a new version of a policy.
  6. Open a role in the IAM console, choose Last Accessed, and list services it never used.