Training module · Draft
Module 7: Access reviews of privileged roles
Status — Draft
This page is still being written. Expect gaps and changes; it is not part of the workbook's production guidance. Last reviewed 2026-11-07.
Part of the Privileged Access learning path. Plan Phase 2, task A2.7, and Phase 3, task A3.7. Workbook Chapter 07. About 55 minutes.
Watch: the narrated deck for this module: Deck · ▶ Watch (about 13 minutes, captions) · MP4 · Script · Captions.
Introduction (3 minutes)
The approval gate controls the use of privilege. Reviews control who keeps the eligibility. Book 04 creates one recurring review per role, Tier 0 monthly and the rest quarterly, with auto-apply on and “remove access” when reviewers don’t respond. A review set up by hand drifts: a reviewer leaves, a duration is shortened, auto-apply is switched off for one cycle and never back on.
In this module you define every privileged review series as code. You show a reviewer what a change will do before it reaches the tenant, apply it with the least permission that can, and hand the ISSO the decisions each review produced.
The emergency accounts hold Global Administrator as permanent active assignments. A review of Global Administrator that covers active assignments, with “remove access” on silence and auto-apply on, removes them if the reviewers miss one month.
- PA-AR-06 fails a review that would do that. A review that covers the emergency accounts’ Global Administrator uses “no change” on silence, and an unreviewed item becomes an evidence finding instead (PA-AR-E04). That’s the owner’s decision, and Book 04, Chapter 08’s Step 8 records the exception.
- PA-AR-E08 reports an emergency account that a reviewer denied.
-Applyrefuses a plan with any Finding, and the code never deletes a series: deleting a definition deletes its decisions, which are evidence.
The federal requirement. The review series and their decisions are the evidence for the plan’s tasks A2.7 and A3.7. They support NIST SP 800-53 AC-6(7) (review of user privileges), AC-2 (account review), AC-5 (separation of duties) and AU-11 (retention). AC-6(7) and AC-2 leave the frequency to the organization; monthly for Tier 0 and quarterly for the rest is the program’s decision D-A6 (design guidance). The full table is the chapter’s Federal requirements and evidence.
| Requirement | KSI | Evidence |
|---|---|---|
| AC-6(7) and AC-2: every privileged role and PRV group has a series with reviewers who aren’t the assignees, a fallback, auto-apply, justification and the tier’s cadence | KSI-IAM-AAM, KSI-IAM-JIT | PA-AR-01, 02, 05, 08, 09 and 14 in findings.json |
| AC-6(7), AC-2 and AC-5: each decision, and what went wrong | KSI-IAM-AAM, KSI-IAM-JIT | decisions.csv, instances.json, and PA-AR-E01 to E08 in findings.json |
| AC-2: the emergency accounts stay usable | KSI-IAM-JIT | The PA-AR-06 refusal |
| CM-3: what a run would change, and an apply that never deletes | KSI-CMT-LMC, KSI-CMT-RMV | plan.json and bodies/; results.json |
| AU-11: the evidence exported before a series is deleted | KSI-MLA-OSM | The exported evidence folder, in the evidence store |
| AC-6, least privilege for each identity | KSI-IAM-ELP | AccessReview.Read.All and Reader to plan and read evidence; write only to apply |
KSIs are FedRAMP 20x indicators for cloud service providers. The agency authorizes its own system and lines this evidence up with what its providers report against them; it doesn’t report KSIs itself (Book 00, Appendix B).
Learning objectives (1 minute)
After this module, you’ll be able to:
- explain why privileged reviews belong in code, and what licensing they need;
- name the series the program defines and the cadence for each tier;
- explain the PA-AR-06 guard and why it changes the program’s own setting;
- read a plan’s actions: Create, Update, Replace, NoChange, Unmanaged and Conflict;
- read the evidence findings, PA-AR-E01 to PA-AR-E08.
Prerequisites (1 minute)
Unit 1: Why reviews, and the series (8 minutes)
Read the chapter’s Why reviews, and why as code and What it costs, and where it runs. The program budgets Microsoft Entra ID Governance for every reviewer and every reviewed privileged user, the stricter of two Learn pages.
Then read The reviewed JSON and The bodies. Note:
- Seven series follow Book 04, Chapter 09’s schedule: Entra roles, PRV group membership (PIM for Groups reviews are in preview), and an Azure role series through a preview API. The Azure series is a Tier 0 series, so it runs monthly with the other Tier 0 roles; Chapter 09 puts the remaining Azure roles at quarterly.
- A role series covers users. Groups that hold the role are reviewed through their own membership series, and PA-AR-14 flags a group without one.
Unit 2: The checks, the plan and the evidence (10 minutes)
Read three sections:
- The checks: PA-AR-01 to PA-AR-14. PA-AR-01 is the first: no reviewers means Graph treats the review as a self-review.
- The dry run, the diff and
-Apply: a scope change or a change of group reviewers is a Replace, which needs a person, because the update API doesn’t take it. - The evidence: each decision becomes a row, and eight checks find missing series, overdue instances, denials not applied, silent defaults, approvals without a reason, self-reviews, bulk decisions, and a denied emergency account.
Exercise: Lab 1, fixtures, no tenant (25 minutes)
This is the workbook’s Lab 1. Work in examples/privileged-access, in one PowerShell 7 session.
Every command in this lab, in order, from examples/privileged-access. Each step below says what it does and what to look for.
# Step 1: Run Learn's tutorial against the fake
Invoke-Pester -Path tests/AccessReviews.Tests.ps1 -FullNameFilter '*(Learn), run as published*' -Output None -PassThru |
Select-Object PassedCount, FailedCount
# Step 2: List each task's permissions
Import-Module ./powershell/PaAccessReviews/PaAccessReviews.psm1
'access-reviews-plan', 'access-reviews-apply', 'access-reviews-evidence' | ForEach-Object { Get-PaArRequiredPermission -Task $_ } |
Format-Table Task, GraphPermissions, AzureRoles
# Step 3: Run the dry run
./Invoke-PaAccessReviews.ps1 -Cloud gcc -Mode Plan -ConfigPath tests/fixtures/ch07/access-reviews.json `
-FixturePath tests/fixtures/ch07/tenant.json -AsOf 2026-10-30 -OutputPath out/reviews-plan
# Step 4: Read the diff
Get-Content out/reviews-plan/plan.json -Raw | ConvertFrom-Json | Format-Table name, action, @{ n = 'changes'; e = { $_.changes -join ', ' } }
# Step 5: Check Learn's body with the example's rules
. ./tests/AccessReviewsSampleHarness.ps1
Test-PaArDefinition -Body (Get-ArTutorialCreateRequest).Body -AsOf 2026-10-30 | Format-Table Rule, Severity
# Step 6: Apply in the fake
./Invoke-PaAccessReviews.ps1 -Cloud gcc -Mode Plan -ConfigPath tests/fixtures/ch07/access-reviews.json `
-FixturePath tests/fixtures/ch07/tenant.json -AsOf 2026-10-30 -OutputPath out/reviews-apply -Apply
Get-Content out/reviews-apply/results.json -Raw | ConvertFrom-Json | Format-Table Name, Action, Result, Note
# Step 7: Read the evidence
./Invoke-PaAccessReviews.ps1 -Cloud gcc -Mode Evidence -ConfigPath tests/fixtures/ch07/access-reviews.json `
-FixturePath tests/fixtures/ch07/tenant.json -AsOf 2026-10-30 -OutputPath out/reviews-evidence
Get-Content out/reviews-evidence/findings.json -Raw | ConvertFrom-Json | Format-Table Rule, Severity, Object
# Step 8: See the lockout guard
$c = Read-PaArConfig tests/fixtures/ch07/access-reviews.json
($c.reviews | Where-Object name -EQ 'PAR-T0-GlobalAdministrator-Monthly').defaultDecision = 'Deny'
$client = New-PaArClient -Cloud gcc -Task access-reviews-apply -Invoke (New-PaArFake -Path tests/fixtures/ch07/tenant.json).Invoke
Invoke-PaArApply -Client $client -Plan (Get-PaArPlan -Client $client -Config $c -AsOf 2026-10-30) -Apply
# Step 9: Run the tests, then delete out
Invoke-Pester -Path tests
Remove-Item -Recurse -Force outRun Learn’s tutorial against the fake:
Invoke-Pester -Path tests/AccessReviews.Tests.ps1 -FullNameFilter '*(Learn), run as published*' -Output None -PassThru | Select-Object PassedCount, FailedCountPassedCount FailedCount ----------- ----------- 11 0The third test shows the tutorial’s POST, sent twice, leaves two series with the same name.
List each task’s permissions:
Import-Module ./powershell/PaAccessReviews/PaAccessReviews.psm1 'access-reviews-plan', 'access-reviews-apply', 'access-reviews-evidence' | ForEach-Object { Get-PaArRequiredPermission -Task $_ } | Format-Table Task, GraphPermissions, AzureRolesTask GraphPermissions AzureRoles ---- ---------------- ---------- access-reviews-plan {AccessReview.Read.All} {Reader} access-reviews-apply {AccessReview.ReadWrite.All} {User Access Administrator} access-reviews-evidence {AccessReview.Read.All} {Reader}Run the dry run:
./Invoke-PaAccessReviews.ps1 -Cloud gcc -Mode Plan -ConfigPath tests/fixtures/ch07/access-reviews.json ` -FixturePath tests/fixtures/ch07/tenant.json -AsOf 2026-10-30 -OutputPath out/reviews-planPlan: 1 Create, 2 Update, 1 Replace, 3 NoChange, 1 Unmanaged Checks: 0 Finding, 1 Warn, 1 Info Dry run: nothing sent (2 write(s) would be sent with -Apply) Written to out/reviews-plan (manifest.json lists every request and file hash)Read the diff:
Get-Content out/reviews-plan/plan.json -Raw | ConvertFrom-Json | Format-Table name, action, @{ n = 'changes'; e = { $_.changes -join ', ' } }name action changes ---- ------ ------- PAR-T0-GlobalAdministrator-Monthly Update settings.instanceDurationInDays PAR-T0-PrivilegedRoleAdministrator-Monthly Create PAR-T1-ExchangeAdministrator-Quarterly Replace reviewers PAR-PRV-T0-GlobalAdmin-Monthly NoChange PAR-PRV-T0-PrivRoleAdmin-Monthly NoChange PAR-PRV-T1-ExchangeAdmin-Quarterly NoChange PAR-AZ-T0-Owner-Platform-Monthly Update settings.instanceDurationInDays PAR-T0-SecurityAdministrator-Monthly UnmanagedThe two writes the dry run counts are the Graph ones; the Azure update needs
-IncludePreview.bodies/holds each request body for review.Check Learn’s body with the example’s rules:
. ./tests/AccessReviewsSampleHarness.ps1 Test-PaArDefinition -Body (Get-ArTutorialCreateRequest).Body -AsOf 2026-10-30 | Format-Table Rule, SeverityRule Severity ---- -------- PA-AR-02 Finding PA-AR-03 Finding PA-AR-05 Finding PA-AR-07 Warn PA-AR-10 WarnApply in the fake:
./Invoke-PaAccessReviews.ps1 -Cloud gcc -Mode Plan -ConfigPath tests/fixtures/ch07/access-reviews.json ` -FixturePath tests/fixtures/ch07/tenant.json -AsOf 2026-10-30 -OutputPath out/reviews-apply -Apply Get-Content out/reviews-apply/results.json -Raw | ConvertFrom-Json | Format-Table Name, Action, Result, NotePlan: 1 Create, 2 Update, 1 Replace, 3 NoChange, 1 Unmanaged Checks: 0 Finding, 1 Warn, 1 Info Applied: 2 write(s) sent; Replace and Unmanaged series are left for a person Written to out/reviews-apply (manifest.json lists every request and file hash) Name Action Result Note ---- ------ ------ ---- PAR-T0-GlobalAdministrator-Monthly Update Sent read back PAR-T0-PrivilegedRoleAdministrator-Monthly Create Sent read back PAR-T1-ExchangeAdministrator-Quarterly Replace Skipped needs a person: export its evidence, delete it in the portal, rerun PAR-PRV-T0-GlobalAdmin-Monthly NoChange Skipped matches the tenant PAR-PRV-T0-PrivRoleAdmin-Monthly NoChange Skipped matches the tenant PAR-PRV-T1-ExchangeAdmin-Quarterly NoChange Skipped matches the tenant PAR-AZ-T0-Owner-Platform-Monthly Update Skipped Azure role reviews use a preview API; pass -IncludePreview PAR-T0-SecurityAdministrator-Monthly Unmanaged Skipped not in the reviewed JSON; never deletedRead the evidence:
./Invoke-PaAccessReviews.ps1 -Cloud gcc -Mode Evidence -ConfigPath tests/fixtures/ch07/access-reviews.json ` -FixturePath tests/fixtures/ch07/tenant.json -AsOf 2026-10-30 -OutputPath out/reviews-evidence Get-Content out/reviews-evidence/findings.json -Raw | ConvertFrom-Json | Format-Table Rule, Severity, ObjectEvidence: 18 decisions in 7 instances; 7 Finding, 2 Warn Written to out/reviews-evidence (manifest.json lists every request and file hash) Rule Severity Object ---- -------- ------ PA-AR-E01 Finding PAR-T0-PrivilegedRoleAdministrator-Monthly PA-AR-E02 Finding PAR-PRV-T0-GlobalAdmin-Monthly PA-AR-E08 Finding PAR-T0-GlobalAdministrator-Monthly / Emergency Access 02 PA-AR-E04 Finding PAR-T0-GlobalAdministrator-Monthly / Frank Ode PA-AR-E05 Finding PAR-T1-ExchangeAdministrator-Quarterly / Carl Ops PA-AR-E03 Finding PAR-T1-ExchangeAdministrator-Quarterly / Frank Ode PA-AR-E04 Warn PAR-T1-ExchangeAdministrator-Quarterly / Gina Lee PA-AR-E06 Finding PAR-PRV-T0-GlobalAdmin-Monthly / Bo Chen PA-AR-E07 Warn PAR-T1-ExchangeAdministrator-Quarterly / reviewer a0000000-0000-4000-8000-000000000013PA-AR-E08: a reviewer denied
Emergency Access 02in October. The chapter’s step 7 explains each row.See the lockout guard:
$c = Read-PaArConfig tests/fixtures/ch07/access-reviews.json ($c.reviews | Where-Object name -EQ 'PAR-T0-GlobalAdministrator-Monthly').defaultDecision = 'Deny' $client = New-PaArClient -Cloud gcc -Task access-reviews-apply -Invoke (New-PaArFake -Path tests/fixtures/ch07/tenant.json).Invoke Invoke-PaArApply -Client $client -Plan (Get-PaArPlan -Client $client -Config $c -AsOf 2026-10-30) -ApplyException: Refusing -Apply: the plan has 1 Finding(s): PA-AR-06 PAR-T0-GlobalAdministrator-MonthlyNothing is sent.
Run the tests, then delete
out:Invoke-Pester -Path tests Remove-Item -Recurse -Force outTests Passed: 330, Failed: 0. Deleteoutbecause the evidence folder names people.
Exercise: Lab 2, your test tenant (5 minutes to read)
Not run by this path’s authors. Follow the workbook’s Lab 2 in a test tenant in the worldwide or GCC cloud with Microsoft Entra ID Governance (a trial will do) and, for the optional Azure step, a test Azure subscription. Never production. Pick a role with no real holders, such as Reports Reader, not Global Administrator.
- What you do: register a reader app and an applier app, write a test JSON with one series, dry-run as the reader, apply after a second person reads
bodies/, close one instance as a test reviewer, and read the evidence. - Permissions:
AccessReview.Read.Allfor the reader;AccessReview.ReadWrite.Allfor the applier; User Access Administrator only for the optional Azure series. - Back-out: step 7 exports the evidence first, then deletes the test series in the admin center (the code never deletes), the apps, their certificates and the test groups, and deletes
out/t-*.
Knowledge check (5 minutes)
- A Global Administrator review covers active assignments, with “Deny” on silence and auto-apply on. What does PA-AR-06 do?
- Nothing; Deny is the program’s default.
- Fails it, because missing one month would remove the emergency accounts’ Global Administrator.
- Turns auto-apply off.
- Removes the emergency accounts from the review.
- A review definition has no reviewers. Why is that a Finding?
- Graph rejects it.
- Graph treats no reviewers as a self-review, and assignees never review themselves.
- It can’t be scheduled.
- It costs more licenses.
- The plan shows a series as Replace. What does
-Applydo with it?- Deletes and recreates it.
- Skips it: the update API doesn’t take a new scope or new group reviewers, so a person exports its evidence and replaces it.
- PUTs the new scope.
- Marks it Unmanaged.
- What does PA-AR-E04 report?
- A reviewer who approved their own access.
- An item not reviewed in a closed instance, so the no-response default decided.
- A series missing from the tenant.
- A denial that didn’t apply.
- b. Taught in The checks, the paragraph after the table, and seen in Lab 1, step 8.
- b. Taught in The checks (PA-AR-01).
- b. Taught in The dry run, the diff and
-Apply, and seen in Lab 1, step 6. - b. Taught in The evidence.
Summary (2 minutes)
You planned seven review series against a fictional tenant, saw Learn’s tutorial body fail the program’s rules, applied two writes and left the Replace and the unmanaged series to a person, read 18 decisions and their findings, and saw the lockout guard refuse a review that would remove the emergency accounts.
The evidence an ISSO keeps: the reviewed JSON and each plan’s bodies/; each apply’s results.json; and each evidence run’s decisions.csv, instances.json and findings.json, in the access-controlled evidence store, because they name people. The full list is in the chapter’s Federal requirements and evidence; before the code ships, work through its checklist.