Access reviews
Access reviews (historically called "certification" or "attestation") are the
periodic and event-driven confirmation that access is still correct. A reviewer
confirms or revokes an assignment; compliance gets a record that the review
happened. There is a single configuration source (CertRule), so the same engine
drives every kind of review.
One engine, three trigger types
There is a single access-review engine. What used to be two parallel producers
is now one rule type with a trigger_type that determines how reviews are
spawned. Every resulting task lands in the same unified review inbox as a
DecisionTask.
trigger_type |
Trigger | Question it answers |
|---|---|---|
continuous |
Periodic (a rolling clock per subject) | "Is this still correct N days after the last approval?" |
event |
A bus event (mover, new assignment, content change, drift, …) | "Something structurally changed — review now" |
campaign |
An explicit campaign with a fixed window | "Review this whole population before the audit deadline" |
The reviewer works continuous and event reviews from one inbox; campaigns are tracked on their own tab. The mental model is that rule-based recertification is the spine and campaigns are the fallback — for audits that must happen on a fixed calendar, or for object sets you deliberately do not want on an automatic clock.
CertRule — the single configuration source
All reusable review configuration lives in CertRule. Its key fields:
| Field | Meaning |
|---|---|
target_kind |
What is being reviewed: grant (an assignment), role, policy, identity_validity, ownership, or account |
trigger_type |
continuous, event, or campaign |
reviewer_chain (inline) or reviewer_chain_id (named) |
Who reviews, in which order, and with what quorum — see Approver chains |
review_scope |
For assignment reviews only — how the items are grouped (see below) |
conditions |
A filter that selects the slice of objects this rule covers (its "bucket") |
auto_action_on_deadline |
What happens to an undecided item at the deadline — differs per target_kind |
Because a rule's conditions scope a slice of the object space, the platform can
expose the gap: every reviewable object should fall under some rule. The ones
that fall under none are never reviewed, and are reported as such — see
Coverage below.
A rule, and what it produces
Concretely. A rule that says every assignment on a critical-risk entitlement is reviewed every ninety days, by the entitlement's owner and then the security team:
target_kind: grant
trigger_type: continuous
conditions: resource.risk_level = "critical"
review_scope: account
interval: 90 days
reviewer_chain: step 1 — object owner (quorum: all)
step 2 — security team (quorum: any)
auto_action: revoke
The continuous sweep walks the assignments that match, skipping any whose clock has not yet run out. For one that has — say R. Okafor holds Production Database Admin, last confirmed ninety-one days ago — it spawns step one only: one task per resolved owner, sharing a fan-out group, carrying the quorum. Step two does not exist yet. It is created once step one's quorum is met, so a chain whose second step is never reached never generates work for the security team.
The reviewer sees the item in their inbox grouped under Okafor's account on that
database, alongside the other assignments held through the same account, because
review_scope is account. If they revoke it, the assignment is removed and the
decision is recorded with them as decider. If nobody decides before the deadline,
auto_action revokes it — and that is recorded too, as an automatic action
rather than as a human decision. A deadline is not an approval, and a record that
blurs the two is worth less than no record. Deadline revokes of undecided items are judged by the
recon breaker ledger: a mass of them is held for release instead of applied.
Target kinds group into three pillars
The target_kind values organise into three business questions:
| Pillar | Question | Target kinds |
|---|---|---|
| Assignment | Does this right still belong to this person? | grant |
| Validity | Should this object still exist? | identity_validity (external and non-human identities) |
| Composition | Should this object still contain these items? | role, policy |
Owner attestation (ownership) sits alongside these: each owner periodically
confirms they still own their objects. Administrators toggle which kinds are
reviewable in the review configuration; a rule can only target a kind that is
enabled.
💡 Tip
Human identities sourced from human resources (HR) are excluded from
identity_validityreviews — HR is authoritative for whether the person still exists. Their access is still reviewed, through the assignment (grant) path.identity_validityreviews apply to non-human and external identities only.
Continuous sweep — dispatched per target kind
The continuous sweep dispatches per target_kind:
| Target kind | What it iterates |
|---|---|
grant |
Each assignment |
role |
Each role |
policy |
Each access grant policy |
identity_validity |
Each identity, filtered by identity type or source system |
ownership |
Each owned object (owner attestation) |
account |
Each account, reviewed as an object |
Each sweep spawns the first step's tasks — one per approver, sharing a fan-out group and carrying quorum metadata. A later step is created lazily once the previous step's quorum is reached.
Event-triggered reviews
Event reviews bind to the assignment-side event catalogue — for example an identity transfer (a mover), a new assignment being granted, drift detected at reconciliation, an emergency-access use, or a role or policy content change. Two default rules are seeded and on by default:
- A mover rule — after a transfer, the person's explicitly granted, context-bound access is reviewed by their manager.
- A new-assignment rule — every fresh assignment is reviewed just in time by the manager. It carries no risk filter; add a condition to narrow it to, say, high-risk entitlements.
An event is a just-in-time review that resets the periodic clock: a second identical event does not spawn a duplicate while a review is already open for that item, and completing the review updates the "last reviewed" timestamp the continuous sweep reads. This is the only producer of mover and new-assignment reviews — there is no separate parallel mechanism.
One question, one task. When a campaign and an event rule ask the same reviewer about the same grant, the reviewer gets one review that lists both sources, not two. The earliest deadline applies. Cancelling the campaign keeps the rule's review open. Another reviewer is another question and gets their own task, and two campaigns over the same grant and reviewer still create two tasks.
Oversight and deciding are different jobs
The access-reviews workbench has five tabs, and none of them is a place to decide anything.
| Tab | Answers |
|---|---|
| Insight | How are the running reviews going — progress, who is behind, what auto-closes soon |
| Campaigns | The fixed-window reviews and their state |
| Rules | The CertRule definitions |
| Coverage | What is reviewed, and what is not |
| Config | Which kinds are reviewable at all |
What the workbench can do is nudge: remind a reviewer, or reassign an item to someone else. Both are recorded. The business admin can remind and escalate on a running campaign, reassign its items (per question and per reviewer), cancel a campaign and apply a suggested cadence, but does not write rules or create, start or sign off a campaign. Creating, starting and signing off a campaign belongs to the compliance officer and the administrator; anyone else sees New campaign disabled, with the reason. Deciding happens in one place — the inbox — and a deep link to the retired "my reviews" tab now lands there rather than reopening a second decision surface.
In the inbox a reviewer can select several access reviews and confirm or revoke them together. The bar uses the lane's own verbs (Confirm / Revoke), takes one shared note, and is capped at 200 items per call. Each item is still decided by the same path as a single click, so the self-review refusal, quorum and audit record are identical, and an item that is refused is reported with its reason while the rest go through. One call covers one lane: review items and access-request approvals cannot be mixed.
Mover carry-reviews ("Moved — keep access?") also appear in the inbox, with Keep and Remove, for the holder's current manager — or the IAM team when no manager can decide. Deciding a carry closes any open review of the same grant as superseded.
Cancelling a campaign withdraws its open items from reviewers' inboxes, and the
deadline sweep never runs the rule's auto_action_on_deadline on an item of a
cancelled campaign.
A revoke is durable: it withdraws the grant's human-decided sources first and then calls the target system. If that call fails or is held, the decision is recorded and reconciliation converges the target, rather than the decision being lost. A revoke is not sent to the target while a rule still owns the grant, since that would create drift. A keep on a rule-derived grant is an attestation and does not add a permanent source.
The split is between two different people on two different days. A compliance lead watching a campaign burn down needs progress, laggards and deadlines across thousands of items; a reviewer needs the five items that are theirs, with enough context to judge them. Serving both from one screen produces a screen that is a weak version of each. More importantly, a second place to decide is a second implementation of what deciding means — the self-review refusal, the quorum arithmetic, the lazy creation of the next step, what gets written to the audit trail. Two implementations do not stay identical; they diverge, and the divergence shows up as an item that was decidable from one screen and not the other, or a decision that recorded less than the other would have. One decision surface is not a user-interface preference. It is how the guarantees stay true.
The tabs are also persona-gated: a reviewer with no configuration rights sees Insight and nothing else, while an auditor sees the observation surfaces without the ability to edit rules.
Coverage is not a two-way answer
The obvious way to report review coverage is as a fraction — this many objects covered, that many not. It is also wrong, because it collapses several very different failures into one number and hides the worse ones behind a green tile.
Every reviewable object on each of the six axes resolves to one of these states:
| State | Meaning | Counts as |
|---|---|---|
| Covered | A rule with a clock matches this object, and the chain reaches the reviewer it names | coverage |
| Covered via fallback | Same, but the named reviewer did not resolve, so the chain fell back to a substitute who did — and the tile says so | coverage |
| Event-only | A rule matches, but every rule that matches is event-triggered. There is no periodic clock | a gap |
| No rule | Nothing matches it. It is never reviewed, and nobody has claimed otherwise | a gap |
| No owner | Only on owner attestation: the object genuinely has nobody to attest it | a gap |
The middle two are the ones that matter, because they are where a green tile can lie.
A rule made from the Coverage tab, or with New rule on the Rules tab, starts as a draft: it covers nothing until you give it conditions and switch it on, so a rule without conditions cannot mark every object of its kind as covered.
Event-only is the sharper of the pair. An object matched only by an event rule looks governed from every angle except the one that counts: there is a rule, it matches, it appears in the configuration as a control a compliance lead can point at in an audit — and it starts a review when a transfer or a new grant happens, and never otherwise. There is no periodic clock: next review reads on event rather than a date, and last certified stays empty until the first review actually fires. That is a routing statement dressed up as an assurance statement, so it is counted with the gaps. The detail pane still shows who would review it when an event fires, because that review is real; the tile simply does not count a review that nothing schedules.
Covered via fallback is the opposite move: it is real coverage, said out loud. A review step can no longer end at nobody. The platform resolves the chain the whole way — through the full ownership cascade, not merely a check for an explicit owner row — and where the named reviewer does not resolve, it takes the tenant's configured fallback for that step's slot, and failing that the IAM terminal, which exists by construction. So an object reached by a substitute is genuinely reviewed and counts as covered, but it is reported as via fallback rather than silently folded into the green: the person who wrote the rule should be able to see that their first choice was not the one who got the task. No owner survives as a state only on owner attestation, which has its own spawner and where "nobody owns this" is the honest answer rather than a routing failure.
A manager step is reported as a distribution over the holders, not as a single verdict: an entitlement held by sixteen people is covered for the holders whose manager resolves and covered via fallback for the rest — routed to that step's configured substitute, and the IAM terminal beyond that — never a gap, since the same "a review step can no longer end at nobody" guarantee applies per holder. One holder with a manager does not make the tile report all sixteen as reaching their manager directly; it reports the real split.
The same resolution drives the tile, the list and the per-object detail, which is why they cannot disagree. A list saying covered next to a detail pane saying nothing covers this object was the earlier version of this page's problem, and one shared answer is what fixed it.
"Our resolver is broken" and "this object has nobody" are different statements, and the code keeps them apart as far as it can: a defect in the resolution propagates rather than being absorbed, so it surfaces as a failure instead of quietly becoming another object in the gap count. A genuinely operational failure — a timeout reaching something it needs — is absorbed so that one bad row cannot take down the whole coverage view, but never silently: it is logged as a failed step. If a coverage figure moves and no configuration changed, the logs are the place that says why.
Review scope — account-first for assignment reviews
Assignments are rooted on an account (an entitlement is held through an account,
not directly by an identity). So an assignment review is presented account-first
by default. review_scope controls the presentation:
review_scope |
Presentation |
|---|---|
account (default) |
Assignments grouped per account — the honest shape, since account-level risk (shared, privileged, orphan) lives at the account layer |
account_with_access |
Adds an "account invalid → revoke all" lead control that revokes the whole account group through the normal per-assignment decisions |
grant_flat |
A deliberate opt-out that hides the account grouping and lists assignments flat |
review_scope is only consulted for target_kind=grant; role, policy and
validity reviews have no account layer. It is purely presentation — the decision
mechanics are unchanged.
What a rule does not review
A rule selects objects, and a few are deliberately left out of the selection:
- Grants a rule already owns. A grant whose whole provenance is derived — an access grant policy match, an SSO coupling, a mover-retention window, a context role or a role-inheritance edge — is not put up for review by the sweep, by campaign generation or by event rules. A revoke on it would not last: the next reconciliation recomputes the same grant. Reviews already open on such a grant are closed as superseded, not decided.
- Grants of a held person. While an identity is locked or not yet activated, the sweep, campaigns and event rules skip its grants, and the rule's item count leaves them out. They come back into scope on the first selection after the hold is lifted. A review that was already open when the hold started is not touched.
Self-review guard
For grant and identity_validity reviews, the reviewer may not be the subject.
The decide action is refused on the server, and the item must be reassigned first.
Ownership attestation is exempt — an owner confirming their own ownership is the
entire point.
A review item is also never routed to someone who cannot decide it: service accounts, AI agents and managers who cannot sign in are dropped as reviewers, and the step falls back as described under Coverage. A "revoke — dormant" suggestion is only shown on systems where usage is tracked, so a directory that keeps no logon time does not produce a dormancy recommendation on every item.
Non-human identity ownership review
On startup, a default rule is seeded that enforces a yearly ownership review for
non-human identities — service accounts, artificial-intelligence agents,
applications, system accounts and internet-of-things devices. It targets
identity_validity, routes to the identity's owner as recorded in the
ownership model, and flags rather than auto-revokes on the deadline. When no owner
can decide, the review goes to a manager on record if the identity carries one,
labelled as a fallback and never as the owner, then to the configured fallback list
and finally to the IAM team. A seeded rule that still carried the earlier
manager-as-proxy chain is moved to the owner chain on existing tenants, and that
change is written to the configuration log. Artificial-intelligence agents
carry a shorter ladder on top of that floor, based on how autonomously they act —
see AI agent governance.
Suggestions: a cadence per risk level, and review patterns
The Suggestions tab of Access reviews proposes a review cadence per risk level from the tenant's own risk levels and review decisions. It starts from critical 90 days, high 180, medium and low 365. A level whose reviewers revoke at least 10% (over at least 30 decisions in a year) is suggested one step more often; a low or medium level that is almost never revoked over two years can be suggested one step less often, at most every 730 days. Critical and high are never suggested less often than today. A dry-run preview shows the effect before one rule is created or changed. When an applied suggestion makes many grants due at once, they are spread over the rule's grace period, a few each day, instead of landing in one reviewer's queue on one morning; rules edited by hand are not spread.
The same tab shows review patterns per reviewer and rule or campaign, by name and without a score: everything kept within seconds, access kept although it is unused (only where usage is measured), and a reviewer who never revokes. Only review oversight sees them. Every suggestion can be dismissed with a reason for 180 days, unless its evidence shifts. The go-live readiness board lists open cadence suggestions.
A person's access review also lists that person's app passwords and tokens and how many still sign in, because keeping the access keeps them too; see one person, many accounts.
Known edges, published rather than discovered
- Coverage counts objects, not risk. An axis at 100% means every object on it resolves to a real reviewer, not that the reviews are good ones or that the intervals are appropriate.
- A rule that matches nothing is not a gap. It reports as a rule with no items, which is indistinguishable from a rule whose population is currently empty. Both are legitimate; neither is flagged.
- The workbench cannot decide, including for someone who has left. An item belonging to a departed reviewer is reassigned, not decided on their behalf. This is a deliberate refusal, and it means a stalled review needs an act by a person rather than an administrative override.
Further reading: