The state of every credential in the account as of a stated moment, the enabled-or-disabled status of every workforce identity in the identity store, and the CloudTrail record of the revocations themselves — the five API calls that actually revoke standing access, each with the time it happened and the administrator who did it
The API supplies the facts, but a human judgement against a documented baseline turns them into evidence.
Fetch
$ aws iam generate-credential-report$ aws iam get-credential-report$ aws identitystore list-users --identity-store-id <IDENTITY_STORE_ID>$ aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=DeleteUser --start-time <T0> --end-time <T1>$ aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=DeleteLoginProfile --start-time <T0> --end-time <T1>$ aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=DeleteAccessKey --start-time <T0> --end-time <T1>$ aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=UpdateAccessKey --start-time <T0> --end-time <T1>$ aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=DeactivateMFADevice --start-time <T0> --end-time <T1>Expected output
From generate-credential-report, a State of STARTED, INPROGRESS or COMPLETE — AWS stores ONE report per account and regenerates it at most once every four hours, so a request made inside that window silently returns the existing report rather than a fresh one. From get-credential-report, Content (the report, base64-encoded CSV), ReportFormat, and GeneratedTime — the timestamp that says what moment the evidence describes, and the field the freshness assertion reads. The CSV columns are: 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 access_key_2, cert_1_active, cert_1_last_rotated, cert_2_active, cert_2_last_rotated, and additional_credentials_info. Note what the report does NOT cover, because it bounds the whole recipe: AWS documents it as including only passwords, THE FIRST TWO access keys per user, MFA devices and X.509 signing certificates — service-specific credentials such as CodeCommit passwords are absent, as is any access key beyond the second, and additional_credentials_info is the only hint that more exist. From identitystore list-users, Users[] with UserId, UserName, ExternalIds and UserStatus, which is ENABLED or DISABLED. There are five lookup calls rather than one because lookup-events accepts exactly ONE lookup attribute per request, and revocation is five different API calls: deleting the user, deleting the console login profile, deleting an access key, setting an access key Inactive, and deactivating the MFA device. From each lookup-events call, Events[] with EventName, EventTime, Username — the REQUESTER who performed the revocation, not the identity revoked — and the CloudTrailEvent JSON, where requestParameters names the user or device that was acted on.
Assertions — what makes it a pass
| Field | Must be | For | Proves |
|---|---|---|---|
| get-credential-report.GeneratedTime | max_age_days 1The credential report being read describes a moment within the last day. AWS regenerates at most once every four hours and returns the stored report inside that window, so calling generate before get does NOT make the result fresh — GeneratedTime is the only field that says how old the evidence is. Substitute the interval your SSP commits to for revocation review. | every row | PS-04 |
Authored opinion, like the commands. Units live in the operator name — max-age-days is days, and nothing here is converted for you.
Map — what it proves
- recipe1
- recipe— an authored recipe collects evidence for this control
- KSI only— a Key Security Indicator reaches it, but no recipe is authored yet
- orphan— no Key Security Indicator reaches it — a person writes it up instead
What else these families can fetch:PS 2/11 →
GovCloud
IAM, the credential report and CloudTrail all operate in both AWS GovCloud (US) Regions and every call here is available; ARNs use partition arn:aws-us-gov. IAM Identity Center is available in both Regions but has NO multi-Region support, so list-users must be run against the Region its instance was enabled in. The CloudTrail calls carry the one real trap: IAM is a global service and its events are recorded in AWS GovCloud (US-West), us-gov-west-1, while Event history and lookup-events show these events in the Region where they occurred — so a DeleteUser lookup issued against us-gov-east-1 returns an empty Events[] and exits zero, which reads exactly like a period in which nobody was separated. The same shape applies in the commercial partition, where those events land in us-east-1.
Notes & assertions
PS-04 is a list of things that happen when someone leaves: disable system access within a defined period, revoke the authenticators, conduct an exit interview, retrieve organizational property, retain access to the information the person worked on, and notify named personnel within a period. AWS holds evidence for the first two and nothing whatever for the other four. And it holds those two only as state and history, never as subject. The credential report says every credential in the account and what shape it is in; CloudTrail says which revocation happened, when, and by which administrator. Five lookups rather than two, because lookup-events takes one lookup attribute per call and revocation is not one API: an estate that follows this recipe's own advice and DISABLES rather than deletes never emits DeleteUser at all, so a collection that watched only for deletions would return an empty Events[] on a correctly executed separation. UpdateAccessKey is in the list for the same reason — setting a key Inactive is a revocation that deletes nothing. What no call knows is that a PERSON was terminated. There is no roster in AWS, no employment status, no separation date — so the join between 'this human left on the 4th' and 'this identity was revoked on the 6th' is made by a human against an HR record, and the period the control defines is checked on that join and not here. This recipe produces the AWS-side column. It is partial for that reason and no assertion below pretends otherwise. That is also why there is exactly one assertion. Any clause of the form 'no user has an active credential' is false on every working account and would be answering about the whole population when the control is about a named few; a clause over 'users your tagging marks as separated' would invent a third tagging convention to answer a question the HR record already answers better. So the single assertion is about the EVIDENCE rather than the estate: that the report describes a recent moment. A credential report is a snapshot with a timestamp, and a stale one is a true statement about a past that has already been superseded. Read the four-hour rule as a collection constraint, not a detail. AWS stores one credential report per account and regenerates it at most once every four hours; a generate call inside that window returns the report you already had. So generate-then-get does not guarantee freshness, GeneratedTime is what does, and a collector that runs the two calls back to back and assumes the second reflects the first is reporting on a state up to four hours old. The report's coverage is narrower than its name suggests, and the gap is exactly where a separation goes wrong. AWS documents it as covering passwords, the first two access keys per user, MFA devices and X.509 signing certificates. A third access key is not in it. Service-specific credentials are not in it. A separated user whose remaining access is a CodeCommit password or a long-term service credential appears in this report as fully revoked. additional_credentials_info is the only signal that anything else exists, and closing that gap needs ListAccessKeys and ListServiceSpecificCredentials per user, which this recipe does not collect and an assessment should. On a well-run estate the IAM half is the small half. Workforce identities live in the identity store and a separation there is UserStatus DISABLED rather than a deletion, which is why the third call is collected: an account whose credential report lists no IAM user for a departed employee, while the identity store still lists them as ENABLED, has not revoked anything — and the credential report, which sees only IAM, cannot show it. Disabling rather than deleting is also the better practice for this control, because it preserves the identifier — see the IA-04 recipe on why a freed name is a hazard. One KSI was deliberately not claimed. KSI-IAM-SUS asks that privileged accounts be disabled or secured IN RESPONSE TO SUSPICIOUS ACTIVITY, and a routine separation is not suspicious activity; the output is identical in both cases and cannot tell an assessor which one it is looking at. KSI-IAM-JIT was dropped for a quieter reason. Its statement asks for a least-privileged, role and attribute-based, and JUST-IN-TIME authorization model, persistently reviewed. Nothing in five commands over STANDING credentials evaluates a just-in-time or attribute-based model; only the 'persistently reviewed' tail is touched, and that tail is already what KSI-IAM-ELP carries here. KSI-IAM-ELP is earned outright: an identity that outlives its holder is precisely a failure of the rule that each user can access only what they need.
References
- IAM User Guide: generate credential reports — the CSV columns, the four-hour regeneration rule, and the documented exclusion of service-specific credentials and any access key beyond the first two https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_getting-report.html
- IAM API: GetCredentialReport — returns Content (the base64-encoded CSV), ReportFormat and GeneratedTime, the timestamp the freshness assertion reads https://docs.aws.amazon.com/IAM/latest/APIReference/API_GetCredentialReport.html
- AWS CLI: identitystore list-users (Users[] with UserId, UserName, ExternalIds and UserStatus ENABLED | DISABLED; requires --identity-store-id) https://docs.aws.amazon.com/cli/latest/reference/identitystore/list-users.html
- AWS CLI: cloudtrail lookup-events — events within the last 90 days; Username is the user or role name of the REQUESTER that called the API https://docs.aws.amazon.com/cli/latest/reference/cloudtrail/lookup-events.html
- AWS GovCloud (US) User Guide: AWS CloudTrail — CloudFront, IAM and AWS STS events are recorded in us-gov-west-1, and lookup-events shows them in the Region where they occurred https://docs.aws.amazon.com/govcloud-us/latest/UserGuide/govcloud-ct.html
- AWS GovCloud (US) User Guide: IAM Identity Center — "Multi-Region support is presently not available" https://docs.aws.amazon.com/govcloud-us/latest/UserGuide/govcloud-sso.html
This AWS mapping is authored opinion (overlay v3.0.0), versioned separately from the dataset and written against ruleset 2026.07.14.01. The upstream FedRAMP dataset names none of these tools.
The Identity and Access Management run (16)
- automatable
- partial — needs judgement
- narrative — no API proves this
Every authored recipe filed under IAM, in the order the plan works them. The mark says how much of the evidence the command produces on its own.
- partialAccount-wide credential report proving MFA is active per principal and that passwords/access keys are rotated within policymonthlyiam-credential-reportIAM
- partialFull snapshot of every IAM user, group, role, and attached/inline policy with their relationships, used to review that granted permissions match least-privilege intentquarterlyiam-account-authorization-detailsIAM
- partialActive unused-access findings identifying IAM roles, access keys, console passwords, and service/action-level permissions that have not been used within the configured age, driving right-sizing and removalcontinuousiam-access-analyzer-unused-accessIAM Access Analyzer
- partialAWS Config compliance result for the managed rule proving every IAM user with a console password has MFA enabledcontinuousconfig-mfa-enabled-console-accessAWS Config · IAM
- partialAWS Config compliance result proving long-lived IAM access keys (including those used by service/non-user identities) are rotated within the maximum agecontinuousconfig-access-keys-rotatedAWS Config · IAM
- partialAWS Config compliance result proving no customer-managed IAM policy grants full administrative access (Allow Action:* on Resource:*)continuousconfig-iam-policy-no-admin-accessAWS Config · IAM
- narrativeDocumented just-in-time / break-glass privilege-elevation process backed by IAM Identity Center permission sets and account assignments, showing privileged access is role/attribute-based, time-bound, and approval-gated rather than standingquarterlyidentity-center-jit-elevation-workflowIAM Identity Center
- partialGuardDuty IAM/credential-abuse findings (detection) paired with CloudTrail records of the responsive action taken to disable or secure the affected privileged principalcontinuousguardduty-suspicious-iam-activity-responseGuardDuty · CloudTrail · IAM
- partialHow operators actually reach the environment from outside it: the managed access paths that exist, the logging and encryption configured on them, the session-by-session record of who used them, and the negative check that no instance is directly reachable insteadweeklyremote-access-authorization-and-monitoringAWS Systems Manager Session Manager · AWS Client VPN · AWS Config · Amazon EC2
- partialEvery way a workforce user can authenticate into the account, counted and named in one pass — how many IAM users and federated trusts exist, which SAML and OIDC providers are registered, whether an IAM Identity Center instance is the workforce entry path — together with the state of the two credentials that belong to no person: the root user's access key and the account's X.509 signing certificatemonthlyidentity-sources-and-root-credential-lockdownAWS IAM · AWS IAM Identity Center · AWS Config
- partialFor every Amazon Cognito directory that fronts non-organizational users: the user pool's multi-factor configuration, and every identity pool's guest-access flag together with the named external providers it will exchange a token forweeklycognito-external-user-authenticationAmazon Cognito · AWS IAM
- partialThe configured ceiling on how long any credential stays valid before its holder must present an authenticator again — MaxSessionDuration on every IAM role, SessionDuration on every IAM Identity Center permission set, and the aws:MultiFactorAuthAge conditions in policy that expire an MFA-backed session independently of the session itselfquarterlysession-lifetime-and-reauthenticationAWS IAM · AWS IAM Identity Center · AWS STS
- partialThe mechanism that ends a temporary or emergency account without anyone deciding to: the AWS Config rule that measures how long an IAM credential has gone unused, the period it is configured with, the remediation configuration proving the revocation fires automatically, and an empty non-compliant set showing nothing has outlived the periodcontinuoustemporary-account-automatic-revocationAWS Config · AWS IAM · AWS Systems Manager Automation
- partialEvery identifier the account has issued, with the date it was assigned and the AWS-generated unique id behind it; the workforce identifiers issued through IAM Identity Center and the external issuer each one came from; and the CloudTrail record of identifiers being deleted, which is the only dated evidence of a name becoming free to reusecontinuousidentifier-assignment-and-reuse-preventionAWS IAM · AWS IAM Identity Center · AWS CloudTrail
- partialThe state of every credential in the account as of a stated moment, the enabled-or-disabled status of every workforce identity in the identity store, and the CloudTrail record of the revocations themselves — the five API calls that actually revoke standing access, each with the time it happened and the administrator who did ityou are herecontinuouspersonnel-separation-access-revocationAWS IAM · AWS IAM Identity Center · AWS CloudTrail
- partialWhat a transferred individual can still reach, and what they have actually used: the reassignment events themselves from CloudTrail (group membership, attached policy, and Identity Center account-assignment changes, each with its time and the administrator who made it), the current Identity Center assignments per permission set, and IAM's service-last-accessed report for the identities involved — a per-principal view of which services the identity is permitted to reach and which of those it has never authenticated to. `iam-access-analyzer-unused-access` answers the same question estate-wide from findings; this answers it for the named principal a transfer is about.continuouspersonnel-transfer-access-reassignmentAWS IAM · AWS IAM Identity Center · AWS CloudTrail