One row per identity that is not a person: service accounts, workload identities, API keys and secrets, OAuth app registrations, and agents. Fill it from the systems, not from memory. Every column comes from the article's eight controls; the blanks record what you found on the date you looked.
Scope (directory, tenant, or system reviewed):
Prepared by / date:
Two lists to reconcile
- [ ] List A: every non-human identity the directory or identity provider knows (service principals, app registrations, agent identities, agent blueprints, service accounts, workload identities).
- [ ] List B: every credential the firm's systems accept that is not a person's login (API keys in schedulers and integrations, static secrets in configuration, OAuth grants in SaaS admin consoles, tokens in CI, certificates for workloads).
- [ ] Every entry in List B that has no match in List A is written into the register below as an unowned identity and given a sponsor or disabled.
- [ ] Every entry in List A with no observed use in the review period is recorded, and its sponsor asked whether it is still needed.
Count in List A / count in List B / unmatched:
Register
| Identity (name or id) | Kind | Sponsor (human) | Purpose (one sentence) | Credential type | Expiry | Roles or permissions | Acts for a person? | Logging destination | Last reviewed |
|---|---|---|---|---|---|---|---|---|---|
Kind: service account, workload identity, API key or secret, OAuth app registration, agent. Credential type: password, static key or secret, client secret, certificate, federated credential, short-lived token, none (issued by a blueprint or role). Write "none" in Expiry only when the platform issues the credential per session; a static credential with no expiry is a finding.
Inventory with a named owner and a purpose
- [ ] Every row has a named human sponsor, not a group alias or a departed person.
- [ ] Every row has a one-sentence purpose that the sponsor confirmed.
- [ ] Ten identities were sampled at random and their sponsors asked what each is for; the answers are recorded below.
- [ ] An identity whose sponsor could not answer, or has left, is disabled with a date recorded.
| Sampled identity | Sponsor asked on | Could explain purpose? | Action taken |
|---|---|---|---|
Least privilege through roles, not standing admin
- [ ] Every identity holding an administrator role, an owner role, or a tenant-wide application permission is listed below with a written justification.
- [ ] No non-human identity holds a global administrator role or its equivalent.
- [ ] Where the identity acts for a person, delegated permissions are used rather than application permissions.
- [ ] Roles are granted through role assignments, app roles, or access packages, never by adding the identity to a role-assignable group.
| Identity | Elevated role or permission | Why it needs it | Narrower alternative considered | Decision |
|---|---|---|---|---|
Short-lived credentials and federation over static secrets
- [ ] Every static key, password, or client secret in the register has an expiry under 90 days and a named rotation owner.
- [ ] Where the platform offers a role, OIDC federation, a projected or bound token, or a blueprint-issued federated credential, the static credential has been replaced and deleted.
- [ ] Secrets with no expiry are counted below; the target is zero.
- [ ] No credential is shared across development, testing, and production, and none is reused across applications.
Secrets with no expiry / rotated this period / replaced by federation:
Conditional access and step-up for elevated actions
- [ ] Access policies are scoped to workload and agent identities, not only to users, wherever the platform supports it.
- [ ] Risk-based blocking for non-human identities is enabled, first in report-only mode, then enforced; the dates are recorded.
- [ ] Actions that change roles, credentials, or policy require a second approval given by a human.
- [ ] An elevated action was attempted from a test identity and was blocked and logged; the record is referenced below.
Policies in report-only / enforced / test attempted on and result:
Logging that names the identity and whom it acts for
- [ ] Every token issuance to a non-human identity is logged with the identity, the acting party where delegation occurs, the resource, and the result.
- [ ] Logs are shipped to a store the identity cannot write to.
- [ ] One tool call or API call was traced from the identity, through the token, to the audit record by identifiers alone; the trace is referenced below.
- [ ] For agents acting on behalf of a person, the record distinguishes the agent from the person.
| Identity | Log source | Store (append-only for the identity?) | Trace completed on | Gap found |
|---|---|---|---|---|
Lifecycle with an expiry and an offboarding test
- [ ] Every grant of access carries an end date, and the sponsor renews it or lets it lapse.
- [ ] When a person leaves, the identities they sponsored are reviewed the same day and reassigned or disabled.
- [ ] A test identity was disabled, and every system it used to reach was checked; each check is recorded below.
- [ ] Deleted or disabled identities are kept in the register with the date, so a later credential found in the wild can be matched.
| Test identity disabled on | System checked | Access ended? | Verified by |
|---|---|---|---|
Kill switch tied to the incident plan
- [ ] For each class of identity, the control that stops the whole class at once is named: a blueprint disable, an access policy block, a revoked trust anchor, a rotated signing key.
- [ ] The incident plan names who may pull each control and how they are reached from a phone.
- [ ] A drill was run, and the time from decision to no token issued was measured and written in the runbook.
| Identity class | Control that stops the class | Who may pull it | Last drill | Minutes to no token issued |
|---|---|---|---|---|
| Service accounts | ||||
| Workload identities | ||||
| API keys and secrets | ||||
| OAuth app registrations | ||||
| Agents |
Quarterly review
- [ ] The two lists were reconciled again and the register updated.
- [ ] No row's last-reviewed date is older than one quarter.
- [ ] Findings from this review were carried into the service inventory and the access review.
Rows added / rows disabled / rows still unowned at close:
Sign-off
Reviewed by / date:
Next review date / change triggers (new agent platform, new integration, departure of a sponsor, new credential type):
This register records the identities that exist and who owns them. It does not decide whether an agent should be connected to firm data in the first place; use the AI connector and data-flow review for that, and the agent observation and containment review once the agent runs.