Skip to main content
This is not an official U.S. federal government website. The Federal Modernization Workbook is independent technical analysis. It does not represent the position of, and has not been reviewed or endorsed by, any federal agency.

Training module · Draft

Module 1: Name one writer for every account state

Gate G1 and workbook Chapter 01: keep the lifecycle rows of the writer register as reviewed JSON, check its shape, and scan the directory audit log for writes by anyone but the named writer. About 50 minutes, with a lab on fixtures and a test tenant lab.

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-08.

Part of the HR-Driven Identity learning path. Gate G1 Writers. Workbook Chapter 01. About 50 minutes.

Watch: the narrated deck for this module: Deck · ▶ Watch (about 13 minutes, captions) · MP4 · Script · Captions.

Introduction (3 minutes)

Your agency’s HR feed sets an account’s active value. A Lifecycle Workflows leaver can disable the same account. A help desk technician can still edit a user’s organization code. Each of those is a writer, and when two of them write the same value, the later one wins. A late active true from the pipeline undoes the workflow’s disable; a help desk edit moves a person’s access without HR knowing.

In this module you fix that before any other gate: you name one writer, and a backstop, for every account-state transition and HR-mastered value, and you test the directory audit log against that list.

The federal requirement. This gate supports NIST SP 800-53 AC-2 and IA-4 for the register, and AU-6 for reviewing writes outside the named writer. The gate’s output is evidence for these controls; your organization still sets their parameters, policy and procedures, and the assessor decides whether each is satisfied. The requirements and the evidence are in the Gate 1 requirements table (under Federal requirements and evidence in Step 1) and in the workbook chapter’s Federal requirements and evidence.

Requirement KSI Evidence
AC-2 and IA-4, the register KSI-IAM-ELP, KSI-IAM-JIT The signed config/writer-register.json and the check output
AU-6, writes outside the named writer KSI-MLA-RVL The scan’s findings.json, writes.json and queries.json
AU-9 and SI-7, evidence nobody edited KSI-MLA-OSM; KSI-SVC-VRI The scan folder’s manifest.json, a SHA-256 per file

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, and it doesn’t report KSIs itself (Book 00, Appendix B). Each KSI lists the row’s control among its related SP 800-53 controls on FedRAMP’s Key Security Indicators page (read November 2, 2026); which file supports which indicator is the program’s mapping (design guidance). The workbook chapter’s table has a row for every lab step.

Learning objectives (1 minute)

After this module, you’ll be able to:

  • explain why one writer per account state comes before every other gate;
  • read the lifecycle rows of the writer register and say which writer and backstop each names;
  • run the register check (WR-01 to WR-09) and the audit-log scan (WS-01, WS-02 and WS-04), and read their findings;
  • say what the scan can’t see, and what covers it.

Prerequisites (1 minute)

Unit 1: Why one writer comes first (8 minutes)

Read the guide’s Step 1. Its table of lifecycle rows is the register you’ll check in the lab: who creates the account, who enables it at hire, who disables it at separation, who deletes it, and who writes the hire date, the leave date, the manager and the certificate binding.

Then read why Book 03 treats a writer as an access decision:

As you read, notice the row for deletion: the post-separation workflow writes it, and inbound provisioning never does.

Unit 2: The register and the scan (6 minutes)

Read three sections of workbook Chapter 01:

  • What it leaves to you: what Learn’s directoryAudits API gives you, and what you build yourself.
  • The checks: the WR rules that check the register’s shape, and the WS rules that test each audit record against it.
  • What the scan doesn’t see: writes made in Active Directory for synced users.

Keep the rule table open during the lab; you’ll match each finding to its rule.

Exercise: Lab 1, fixtures, no tenant (20 minutes)

This is the workbook’s Lab 1. Work in examples/hr-lifecycle. The audit records are fictional.

NoteEvery command in Lab 1

Every command in this lab, in order, from examples/hr-lifecycle. Each step below says what it does and what to look for.

# Step 1
python python/graph_export.py --task writers-scan --list

# Step 2
python python/writer_register.py check --register config/writer-register.json

# Step 3
python python/writer_register.py scan --register config/writer-register.json --audit tests/fixtures/ch01/directory-audits.json --since 2026-10-01 --until 2026-10-31 --cloud gcc --out out/ch01

# Step 4
python -c "import json; [print(r['Rule'], r['Severity'], r['Object'], '|', r['Issue']) for r in json.load(open('out/ch01/findings.json'))]"

# Step 5
python -c "import json; print(json.load(open('out/ch01/queries.json'))[2])"

# Step 6
python -m pytest tests -q

# Step 6
rm -rf out
  1. List what the scan reads, and with what permission:

    python python/graph_export.py --task writers-scan --list
    Task writers-scan: AuditLog.Read.All, Directory.Read.All
      GET /auditLogs/directoryAudits  [AuditLog.Read.All]
      GET /auditLogs/provisioning  [AuditLog.Read.All, Directory.Read.All]
  2. Check the register:

    python python/writer_register.py check --register config/writer-register.json

    Expect Register: 0 Finding, 0 Warn, 0 Info and exit code 0.

  3. Scan October’s audit records:

    python python/writer_register.py scan --register config/writer-register.json --audit tests/fixtures/ch01/directory-audits.json --since 2026-10-01 --until 2026-10-31 --cloud gcc --out out/ch01
    Protected writes: 11 (writer 5, backstop 1, second writer 4, forbidden 1)
    Writers: 5 Finding, 1 Warn, 0 Info
    Written to out/ch01 (manifest.json lists every file's SHA-256)

    The exit code is 2, because the fixture holds findings.

  4. Read the findings:

    python -c "import json; [print(r['Rule'], r['Severity'], r['Object'], '|', r['Issue']) for r in json.load(open('out/ch01/findings.json'))]"

    Expect four WS-01 Findings (a help desk disable, an Exchange administrator’s leave-date edit, a help desk manager change and a fix-up script’s organization code), one WS-04 Finding (a delete by inbound provisioning, which the register forbids) and one WS-02 Warn (the backstop enabled a hire). The workbook lists the six lines verbatim.

  5. Read one saved query:

    python -c "import json; print(json.load(open('out/ch01/queries.json'))[2])"

    The third query is the directoryAudits filter for the disabled-at-separation row: activityDisplayName eq 'Disable account' within the window, allowed for inbound-provisioning and lifecycle-workflows. Gate 7’s scheduled review runs these queries; Module 7’s lab reads the scan’s findings.

  6. Run the tests:

    python -m pytest tests -q

    Expect 57 passed. Delete out when you’re done:

    rm -rf out

Exercise: Lab 2, your test tenant (5 minutes to read)

Not run by the authors. Follow the workbook’s Lab 2 in a worldwide or GCC test tenant, never production.

  • What you do: make two known writes in the admin center, export the audit window, confirm the modifiedProperties names the register expects, put your tenant’s service principal IDs in a copy of the register, and scan. Both admin center changes should appear as WS-01.
  • Permissions: a reader app with a certificate, holding AuditLog.Read.All and Directory.Read.All as application permissions with admin consent, and nothing else.
  • Back-out: restore the test users’ values, remove the two permissions, delete the reader app and its certificate, and delete the export and output folders.

Lab 2 matters more here than in most modules: the property names in the register aren’t documented on Learn, and a wrong name makes the scan report silence that looks clean (Dangers).

Knowledge check (5 minutes)

  1. Your HR feed and the leaver workflow can both disable an account. What does the register record for the disabled-at-separation row?
    1. Both, as equal writers, so either can act first
    2. One writer, with the other recorded as its backstop
    3. Neither; account state is left to the help desk
    4. Whichever wrote it most recently in the audit log
  2. The scan reports a WS-04 Finding for a deleted account. What happened?
    1. A writer the row forbids, inbound provisioning, deleted the account
    2. The backstop deleted the account before the writer did
    3. The delete failed and was retried
    4. The delete fell outside the scan’s window
  3. Which writes does the Entra audit-log scan not see?
    1. Writes by Lifecycle Workflows
    2. Writes by the HR provisioning app
    3. Writes made in Active Directory for synced users
    4. Writes by a help desk administrator in the admin center
  4. Why must Lab 2 confirm each auditProperty name against a real record before the scan counts as evidence?
    1. Graph rejects a filter with an unknown property name
    2. Learn doesn’t document the names, and a wrong one makes the scan report nothing for that value
    3. The names change every 30 days
    4. The register check fails until the names are confirmed
  1. b. One writer per row, with a backstop that isn’t the writer itself (WR-01, WR-06). Taught in Step 1’s lifecycle rows and The checks.
  2. a. WS-04 flags a write by a writer the row forbids; the register forbids deletion by inbound provisioning. Taught in The checks.
  3. c. AD writes are seen only by AD change auditing; the guide’s interim path names the sync agent as a writer and reviews the OU ACLs. Taught in What the scan doesn’t see.
  4. b. The property names are the program’s reading, not Learn’s, and silence looks clean. Taught in Verified facts and Dangers.

Summary (2 minutes)

You checked a writer register’s shape, scanned a month of audit records against it, and read the saved query Gate 7 runs. You saw four second writers, one forbidden delete and one backstop that acted first.

The evidence an ISSO keeps for Gate 1: the signed register with its lifecycle rows and the versioned scope filter; the check output; the scan’s output folder with its manifest and the saved queries.json; and the AD auditing settings, which this code doesn’t read. The full list is in the workbook’s Federal requirements and evidence.

Next: Module 2: Run contractors from their own source.

Back to top