# What 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.

> FedRAMP Consolidated Rules for 2026 v2026.07.14.01 · updated 2026-07-14
> Canonical page: /collect/personnel-transfer-access-reassignment

Recipe id: `personnel-transfer-access-reassignment` · cadence continuous · partial

> **Authored opinion.** AWS overlay v3.0.0, written
> against dataset 2026.07.14.01. The upstream
> FedRAMP rules name none of these tools; this mapping is ours.

## What it proves

- KSI `KSI-IAM-ELP`
- control `ps-5`

## Collection

Kind: `cli`

```sh
# lookup-events-add-user-to-group
aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=AddUserToGroup --start-time <T0> --end-time <T1>
# lookup-events-remove-user-from-group
aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=RemoveUserFromGroup --start-time <T0> --end-time <T1>
# lookup-events-attach-user-policy
aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=AttachUserPolicy --start-time <T0> --end-time <T1>
# lookup-events-detach-user-policy
aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=DetachUserPolicy --start-time <T0> --end-time <T1>
# lookup-events-create-account-assignment
aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=CreateAccountAssignment --start-time <T0> --end-time <T1>
# lookup-events-delete-account-assignment
aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=DeleteAccountAssignment --start-time <T0> --end-time <T1>
# list-permission-sets
aws sso-admin list-permission-sets --instance-arn <INSTANCE_ARN>
# list-account-assignments
aws sso-admin list-account-assignments --instance-arn <INSTANCE_ARN> --account-id <ACCOUNT_ID> --permission-set-arn <PERMISSION_SET_ARN>
# generate-service-last-accessed-details
aws iam generate-service-last-accessed-details --arn <PRINCIPAL_ARN> --granularity ACTION_LEVEL
# get-service-last-accessed-details
aws iam get-service-last-accessed-details --job-id <JOB_ID>
```

## Expected output

From each lookup-events call, Events[] with EventName, EventTime, Username — the REQUESTER who made the change, never the identity changed — and CloudTrailEvent, whose requestParameters names the user, group, policy or account assignment acted on. Six calls rather than one because lookup-events accepts exactly ONE lookup attribute per request and a reassignment is six different APIs across two services; the window is bounded at 90 days by the API. From list-permission-sets, PermissionSets[] of ARNs. From list-account-assignments, AccountAssignments[] with AccountId, PermissionSetArn, PrincipalId and PrincipalType (USER or GROUP) — for ONE permission set in ONE account, which is why list-permission-sets precedes it: the assignment set of an account is the union over its permission sets and no single call returns it. From generate-service-last-accessed-details, a JobId (36 characters); from get-service-last-accessed-details, JobStatus (IN_PROGRESS | COMPLETED | FAILED) and ServicesLastAccessed[] with ServiceName, ServiceNamespace, LastAuthenticated, LastAuthenticatedEntity and TotalAuthenticatedEntities, plus TrackedActionsLastAccessed[] when the request asked for ACTION_LEVEL. A service the principal is permitted to call and has never called comes back with the entry PRESENT and LastAuthenticated ABSENT, which is the shape that makes unused-but-granted access visible at all.

## Assertions

- {"field":"get-service-last-accessed-details.ServicesLastAccessed[?!not_null(LastAuthenticated)]","op":"count_eq","value":0,"controls":["ps-5"],"description":"After the reassignment review, the transferred principal holds no permission it has never used. The offender form — the services with no LastAuthenticated at all — because a service the principal has never called is listed WITHOUT the most-recent-attempt details rather than with a null timestamp, so a projection over LastAuthenticated drops exactly the rows this clause exists to find. Read a non-zero count as the review's work list, not as a failure: the control is satisfied by acting on it within the period the SSP defines, and whether the individual still needs each one is the HR judgement no command makes."}
- {"field":"get-service-last-accessed-details.JobStatus","op":"eq","value":"COMPLETED","controls":["ps-5"],"description":"The last-accessed report actually finished. The generate call returns a JobId immediately and the get call answers IN_PROGRESS until the report exists — a collector that reads ServicesLastAccessed[] from an unfinished job reads an empty array, which would make the clause above vacuously true at the exact moment it has learned nothing."}

## GovCloud

IAM, CloudTrail and IAM Identity Center all operate in both AWS GovCloud (US) Regions; ARNs use partition arn:aws-us-gov, and the sso-admin ARN patterns accept it. Two partition traps, both real here. IAM is global and its events are recorded in AWS GovCloud (US-West), us-gov-west-1, while lookup-events shows events in the Region where they occurred — so the four IAM lookups issued against us-gov-east-1 return an empty Events[] and exit zero, which reads exactly like a period in which nobody transferred. The equivalent commercial Region is us-east-1. Second, IAM Identity Center has no multi-Region support in GovCloud, so the sso-admin calls must be issued against the Region its instance was enabled in. What AWS documents is the absence of multi-Region support; what a call issued against the other Region actually does is not documented on that page and this recipe does not guess. Establish which Region holds the instance before collecting, rather than inferring it from a response.

## Notes

PS-05 is about a person moving inside the organization: the access that fitted the old role is reviewed, what is no longer needed is removed, and the transfer is completed within a period the provider defines. AWS holds two-thirds of that and cannot hold the rest.

What it holds is the CHANGE and the STATE. CloudTrail records the six APIs a reassignment actually travels through — two for group membership, two for attached user policies, two for Identity Center account assignments — each with a timestamp and the administrator who made it. Identity Center holds the assignments as they stand today. What no call knows is that a person MOVED. There is no roster in AWS, no job title, no transfer date, so the join between 'this human changed teams on the 4th' and 'this principal lost the finance permission set on the 9th' is made by a human against an HR record, and the period the control defines is checked on that join. This recipe produces the AWS-side column and the rating says so.

The third call set is the one that earns its place. A separation is visible as an absence; a transfer is visible as a RESIDUE — the old permission that nobody removed because nothing failed when it stayed. Service-last-accessed is the signal that names it per principal: for a principal it lists every service the identity could reach under its permissions policies, with the last time it authenticated, and an entry with no LastAuthenticated is a permission granted and never exercised. Run against the transferred principal it turns 'review the access that no longer fits' from an interview into a list. It is not the only route to that question — `iam-access-analyzer-unused-access` answers it estate-wide from findings — but it is the one that answers it about a NAMED identity, which is the unit a transfer is measured in.

Read its four documented limits before quoting it. It reports for at least the last 400 days, and less in a Region that began supporting the feature within the last year — so a young Region reports a shorter history than the reviewer assumes. Recent activity usually appears within four hours, so a report pulled immediately after a transfer describes the state before it. It records ATTEMPTS and not successes: a denied call still marks a service as accessed, and AWS names CloudTrail as the authoritative source for whether a call succeeded. And it applies permissions-policy logic ONLY — resource-based policies, ACLs, Organizations policies, permissions boundaries and STS assume-role trust are all excluded, so an identity that reaches a bucket purely through a bucket policy is invisible to it. That last one bounds the whole claim: 'no unused permissions' from this report is a statement about identity policies, not about reach.

list-account-assignments takes an account id AND a permission set ARN, and returns the assignees of that one pair. There is no call that returns an account's assignments whole, which is why list-permission-sets runs first and the collection is a loop. A single unlooped call would be a slice of one permission set presented as an account's access, and an assertion over it would be green on an estate where every other permission set is wrong.

The authentication indicators are untouched here — nothing collected reads a factor. KSI-IAM-ELP is earned outright: a permission that fits a role the holder has left is exactly the failure of 'each user can access only what they need'. KSI-IAM-JIT is deliberately NOT claimed, on this overlay's own precedent: the PS-04 recipe dropped it because nothing evaluated over STANDING credentials examines a just-in-time or attribute-based model, and only the 'persistently reviewed' tail is touched — a tail KSI-IAM-ELP already carries. Everything collected here is standing too: standing credentials, standing group membership, standing account assignments. The just-in-time half is the Identity Center elevation recipe's to answer, and claiming it here would have had two sibling recipes taking opposite positions on one indicator over one class of telemetry.

## References

- {"title":"AWS CLI: cloudtrail lookup-events — one lookup attribute per request, events within the last 90 days, Username is the REQUESTER that called the API","url":"https://docs.aws.amazon.com/cli/latest/reference/cloudtrail/lookup-events.html"}
- {"title":"IAM API: GenerateServiceLastAccessedDetails — Granularity SERVICE_LEVEL | ACTION_LEVEL, at least 400 days of history, activity appears within about four hours, records ATTEMPTS including denied ones, and applies permissions-policy logic only (resource-based policies, ACLs, Organizations policies, permissions boundaries and assume-role trust excluded)","url":"https://docs.aws.amazon.com/IAM/latest/APIReference/API_GenerateServiceLastAccessedDetails.html"}
- {"title":"IAM API: GetServiceLastAccessedDetails — JobStatus is IN_PROGRESS | COMPLETED | FAILED, and a service with no access attempt is listed WITHOUT details about the most recent attempt (the shape both assertions read)","url":"https://docs.aws.amazon.com/IAM/latest/APIReference/API_GetServiceLastAccessedDetails.html"}
- {"title":"IAM Identity Center API: ListAccountAssignments — requires both AccountId and PermissionSetArn, returns AccountAssignments[] with PrincipalType and PrincipalId for that one pair","url":"https://docs.aws.amazon.com/singlesignon/latest/APIReference/API_ListAccountAssignments.html"}
- {"title":"AWS GovCloud (US) User Guide: AWS CloudTrail — IAM and AWS STS events are recorded in us-gov-west-1 and lookup-events shows them in the Region where they occurred","url":"https://docs.aws.amazon.com/govcloud-us/latest/UserGuide/govcloud-ct.html"}
- {"title":"AWS GovCloud (US) User Guide: IAM Identity Center — multi-Region support is not available, so the instance answers only in the Region it was enabled in","url":"https://docs.aws.amazon.com/govcloud-us/latest/UserGuide/govcloud-sso.html"}
