Training module · Draft
Module 9: Govern AI agent identities
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-09.
Part of the Non-Person Identity learning path. The guide’s AI agents section. Workbook Chapter 09. About 65 minutes.
Watch: the narrated deck for this module: Deck · ▶ Watch (about 15 minutes, captions) · MP4 · Script · Captions.
Also: the chapter’s narrated tour, about 12 minutes: ▶ Watch.
Introduction (3 minutes)
AI agents are non-person identities, and they follow every gate before this module. They add three things. An agent can act, so a sponsor who hasn’t confirmed within ten business days of a handover makes it an orphan, an extra trigger on top of the program’s disable-first orphan rule. A blueprint holds the credentials for every agent made from it, so one secret on it is every agent’s secret. And by default the person who creates an agent becomes its sponsor and owner, so creation has to be gated.
In this module you check a fictional tenant’s agents against the program’s rules, see the same rules run where the cloud has no agent identities, and plan two report-only agent Conditional Access policies.
The federal requirement. The guide’s AI agents section adds to the gates, and its Evidence table carries SCuBA MS.AAD.9.1v1 for risky agents. The workbook chapter’s Federal requirements and evidence table has a row for every lab step.
| Requirement | KSI | Evidence |
|---|---|---|
| AC-2 Account Management; OMB M-25-21, section 3(b)(v) | KSI-IAM-SNU | ai-identities.json: sponsor, sponsoring group, technical owner and record for every AI identity |
| AC-2; AC-5 Separation of Duties | KSI-IAM-SNU; KSI-IAM-JIT (AC-5) | findings.json, NPI-AI-03 to -09 |
| IA-5(2); AC-6; AC-6(5) Privileged Accounts | KSI-IAM-SNU, KSI-SVC-ASM; KSI-IAM-ELP, KSI-IAM-JIT | findings.json, NPI-AI-10 to -15 |
| CM-8 System Component Inventory; AC-2(4) Automated Audit Actions | KSI-PIY-GIV (CM-8); KSI-MLA-LET, KSI-MLA-RVL (AC-2(4)) | findings.json, NPI-AI-21 to -23: no shadow AI identity |
| AC-3 Access Enforcement; SCuBA MS.AAD.9.1v1; CM-3 | KSI-IAM-ELP, KSI-IAM-JIT; KSI-CMT-LMC, KSI-CMT-RVP | agent-ca-plan.json; results.json and manifest.json from the dry run and the apply |
| SA-9 External System Services | KSI-SCR-MIT, KSI-SCR-MON | config/agent-id-capabilities-ch09.json; Get-NpiAgentCapability |
KSIs are FedRAMP 20x indicators for cloud service providers. The agency lines this evidence up with what its providers report against them; it doesn’t report KSIs itself. Which file supports which indicator is the program’s mapping (design guidance). The guide’s Evidence section explains both.
Learning objectives (1 minute)
After this module, you’ll be able to:
- describe a blueprint, an agent identity and a sponsor, and why the blueprint is the credential boundary;
- explain decision D-NP6 as data, and what the code does in a cloud without Agent ID;
- run the AI identity check and read its findings, including the P1 drift;
- explain the ten-business-day sponsor rule and what happens to an orphaned agent;
- show that the agent Conditional Access plan is report-only and runs only by its approved hash.
Prerequisites (1 minute)
Unit 1: What the guide adds for agents (8 minutes)
Read the guide’s AI agents section: sponsors and blueprints, gated creation, credentials, recertification by expiry, and availability by cloud. Then read:
Unit 2: The rules as code (14 minutes)
Read the workbook chapter in this order:
- What Microsoft Entra Agent ID is, and its stage: which pieces are GA, which are preview, and what GCC lists.
- Decision D-NP6 as data: AgentId mode and Fallback mode.
- The program’s rules the code holds (design guidance).
- The checks: NPI-AI-01 to -23, the ten business days, and standing privileged roles.
- The agent Conditional Access plan and the separate write path.
Exercise: Lab 1, fixtures, no tenant (25 minutes)
This is the workbook’s Chapter 09, Lab 1, which explains each result. Its step 1 here makes Chapters 01 and 02’s runs, so the chapter’s steps 1 to 10 are steps 2 to 11 below. Work in examples/non-person-identity, in PowerShell 7. The tenant and its agents are fictional, and -Apply changes only a fake.
Every command in this lab, in order, from examples/non-person-identity. Each step below says what it does and what to look for.
# Step 1: Make Chapter 01's register and Chapter 02's ownership run
./Invoke-NpiInventory.ps1 -Cloud gcc -FixturePath tests/fixtures/estate.json -OutputPath out/inventory
./Invoke-NpiOwnership.ps1 -Cloud gcc -RegisterRun out/inventory -FixturePath tests/fixtures/estate.json -InputPath tests/fixtures/inputs -OutputPath out/ownership
# Step 2: Send Learn's samples to the fake
Invoke-Pester -Path tests/NpiAgentId.Tests.ps1 -FullNameFilter '*Learn*' -Output None -PassThru |
Select-Object PassedCount, FailedCount, NotRunCount
# Step 3: List what each task needs
Import-Module ./powershell/NpiAgentId/NpiAgentId.psm1
foreach ($t in 'agent-id', 'agent-ca-apply') { $p = Get-NpiAgentRequiredPermission -Task $t; "$t : " + ($p.GraphPermissions -join ', ') }
# Step 4: Read D-NP6 for each cloud
'global','gcc','gcchigh','dod' | ForEach-Object { $c = Get-NpiAgentCapability -Cloud $_
[pscustomobject]@{ Cloud = $_; Mode = $c.Mode; AgentIdentities = $c.AgentIdentities.stage; AgentCA = $c.AgentConditionalAccess.stage; SponsorWorkflows = $c.SponsorWorkflows.stage } } | Format-Table
# Step 5: Check the AI identities and build the plan
./Invoke-NpiAgentId.ps1 -Cloud gcc -RegisterRun out/inventory -OwnershipRun out/ownership -FixturePath tests/fixtures/ch09/agent-estate.json -InputPath tests/fixtures/ch09/inputs -OutputPath out/agent-id
# Step 6: Read the findings
Get-Content out/agent-id/findings.json -Raw | ConvertFrom-Json | Where-Object Severity -eq 'Finding' | Group-Object Rule | Format-Table Name, Count
# Step 7: Read the sponsor fields Chapter 02 recorded, filled
Get-Content out/agent-id/ai-identities.json -Raw | ConvertFrom-Json | Format-Table Name, FederalSponsor, TechnicalOwner, InventoryRecord, Handshake, SponsorState
# Step 8: Run the D-NP6 fallback in GCC High
./Invoke-NpiInventory.ps1 -Cloud gcchigh -FixturePath tests/fixtures/estate.json -OutputPath out/inventory-gcchigh
./Invoke-NpiOwnership.ps1 -Cloud gcchigh -RegisterRun out/inventory-gcchigh -FixturePath tests/fixtures/estate.json -InputPath tests/fixtures/inputs -OutputPath out/ownership-gcchigh
./Invoke-NpiAgentId.ps1 -Cloud gcchigh -RegisterRun out/inventory-gcchigh -OwnershipRun out/ownership-gcchigh -FixturePath tests/fixtures/ch09/agent-estate.json -InputPath tests/fixtures/ch09/inputs -OutputPath out/agent-id-gcchigh
# Step 9: Dry-run the agent Conditional Access plan with its approved hash
$sha = (Get-FileHash out/agent-id/agent-ca-plan.json -Algorithm SHA256).Hash.ToLower()
./Invoke-NpiAgentCaAction.ps1 -Cloud gcc -PlanPath out/agent-id/agent-ca-plan.json -ApprovedSha256 $sha -FixturePath tests/fixtures/ch09/agent-estate.json -OutputPath out/agent-ca-dryrun
# Step 10: Apply it to the fake, then try a hash nobody approved
./Invoke-NpiAgentCaAction.ps1 -Cloud gcc -PlanPath out/agent-id/agent-ca-plan.json -ApprovedSha256 $sha -FixturePath tests/fixtures/ch09/agent-estate.json -OutputPath out/agent-ca-apply -Apply
./Invoke-NpiAgentCaAction.ps1 -Cloud gcc -PlanPath out/agent-id/agent-ca-plan.json -ApprovedSha256 ('0' * 64) -FixturePath tests/fixtures/ch09/agent-estate.json -OutputPath out/agent-ca-x
# Step 11: Run the tests
Invoke-Pester -Path tests
# After the steps
Remove-Item -Recurse -Force outMake Chapter 01’s register and Chapter 02’s ownership run:
./Invoke-NpiInventory.ps1 -Cloud gcc -FixturePath tests/fixtures/estate.json -OutputPath out/inventory ./Invoke-NpiOwnership.ps1 -Cloud gcc -RegisterRun out/inventory -FixturePath tests/fixtures/estate.json -InputPath tests/fixtures/inputs -OutputPath out/ownershipThe last line is
Plan SHA-256: 1e1799d7181bd1a3dc271c645fff8a60fc741ab1ebc219bb3011cc25953bad3e.Send Learn’s samples to the fake:
Invoke-Pester -Path tests/NpiAgentId.Tests.ps1 -FullNameFilter '*Learn*' -Output None -PassThru | Select-Object PassedCount, FailedCount, NotRunCountExpect
PassedCount 13,FailedCount 0,NotRunCount 49.List what each task needs:
Import-Module ./powershell/NpiAgentId/NpiAgentId.psm1 foreach ($t in 'agent-id', 'agent-ca-apply') { $p = Get-NpiAgentRequiredPermission -Task $t; "$t : " + ($p.GraphPermissions -join ', ') }Expect ten read permissions for
agent-id, none ending inReadWrite, andPolicy.Read.All, Policy.ReadWrite.ConditionalAccessforagent-ca-apply.Read D-NP6 for each cloud:
'global','gcc','gcchigh','dod' | ForEach-Object { $c = Get-NpiAgentCapability -Cloud $_ [pscustomobject]@{ Cloud = $_; Mode = $c.Mode; AgentIdentities = $c.AgentIdentities.stage; AgentCA = $c.AgentConditionalAccess.stage; SponsorWorkflows = $c.SponsorWorkflows.stage } } | Format-TableCloud Mode AgentIdentities AgentCA SponsorWorkflows ----- ---- --------------- ------- ---------------- global AgentId GA (April 2026) Preview (policy builder label) GA (May 2026) gcc AgentId GA (April 2026) Preview (policy builder label) GA (May 2026) gcchigh Fallback Not published Not published Not published dod Fallback Not published Not published Not publishedCheck the AI identities and build the plan:
./Invoke-NpiAgentId.ps1 -Cloud gcc -RegisterRun out/inventory -OwnershipRun out/ownership -FixturePath tests/fixtures/ch09/agent-estate.json -InputPath tests/fixtures/ch09/inputs -OutputPath out/agent-idD-NP6 (Proposed): AgentId mode in cloud 'gcc'; agent Conditional Access Preview (policy builder label); agent risk Preview AI identities: 8 checked (4 AgentIdentity, 3 AgentIdentityBlueprint, 1 ServicePrincipal); Chapter 02 sponsor fields filled: 2 of 2 Findings: 22 Finding (4 P1, 2 P2), 6 Warn, 4 Info. Orphan candidates for Chapter 02: 2 Agent CA plan: Unapproved InPlace, HighRisk Create; nothing sent Plan SHA-256: 5a8002467655390a12ee9f8967a82d52e40234adf3a9a74ea37952b34b14073bRead the findings:
Get-Content out/agent-id/findings.json -Raw | ConvertFrom-Json | Where-Object Severity -eq 'Finding' | Group-Object Rule | Format-Table Name, CountExpect 16 rules with Findings. Look for these: NPI-AI-10 twice (Case Triage Blueprint’s secret, and Correspondence Drafting’s app registration), NPI-AI-13 once (Case Triage Blueprint inherits all Graph roles), NPI-AI-16 once (Records Search: its sponsor left on October 9 and nobody confirmed by October 26), and the P1 drift, NPI-AI-21, NPI-AI-22 twice and NPI-AI-23. Benefits Q&A Agent and its blueprint pass every rule.
Read the sponsor fields Chapter 02 recorded, filled:
Get-Content out/agent-id/ai-identities.json -Raw | ConvertFrom-Json | Format-Table Name, FederalSponsor, TechnicalOwner, InventoryRecord, Handshake, SponsorStateExpect eight rows. Case Triage Agent has
carol@contoso.exampleas both sponsor and technical owner. Records Search Agent isOrphaned. Sandbox Helper has no sponsor, record or handshake.Run the D-NP6 fallback in GCC High (Chapters 01 and 02 again, in that cloud):
./Invoke-NpiInventory.ps1 -Cloud gcchigh -FixturePath tests/fixtures/estate.json -OutputPath out/inventory-gcchigh ./Invoke-NpiOwnership.ps1 -Cloud gcchigh -RegisterRun out/inventory-gcchigh -FixturePath tests/fixtures/estate.json -InputPath tests/fixtures/inputs -OutputPath out/ownership-gcchigh ./Invoke-NpiAgentId.ps1 -Cloud gcchigh -RegisterRun out/inventory-gcchigh -OwnershipRun out/ownership-gcchigh -FixturePath tests/fixtures/ch09/agent-estate.json -InputPath tests/fixtures/ch09/inputs -OutputPath out/agent-id-gcchighThe last command prints
D-NP6 (Proposed): Fallback mode in cloud 'gcchigh'andAI identities: 1 checked (1 ServicePrincipal). No agent identity read is sent.Dry-run the agent Conditional Access plan with its approved hash:
$sha = (Get-FileHash out/agent-id/agent-ca-plan.json -Algorithm SHA256).Hash.ToLower() ./Invoke-NpiAgentCaAction.ps1 -Cloud gcc -PlanPath out/agent-id/agent-ca-plan.json -ApprovedSha256 $sha -FixturePath tests/fixtures/ch09/agent-estate.json -OutputPath out/agent-ca-dryrunDryRun: 1 WouldSend WouldSend HighRisk POST https://graph.microsoft.com/beta/identity/conditionalAccess/policiesApply it to the fake, then try a hash nobody approved:
./Invoke-NpiAgentCaAction.ps1 -Cloud gcc -PlanPath out/agent-id/agent-ca-plan.json -ApprovedSha256 $sha -FixturePath tests/fixtures/ch09/agent-estate.json -OutputPath out/agent-ca-apply -Apply ./Invoke-NpiAgentCaAction.ps1 -Cloud gcc -PlanPath out/agent-id/agent-ca-plan.json -ApprovedSha256 ('0' * 64) -FixturePath tests/fixtures/ch09/agent-estate.json -OutputPath out/agent-ca-xExpect
Apply: 1 Sent, withCreated ca000000-0000-4000-8000-000000000103 report-only. Then the refusal:The plan's SHA-256 is 5a800246…, not the approved 0000…; refusing to run a plan nobody reviewed.Run the tests:
Invoke-Pester -Path testsExpect 513 tests passed, 0 failed.
Delete out when you’re done:
Remove-Item -Recurse -Force outExercise: Lab 2, your test tenant (5 minutes to read)
Not run by the authors. Follow the workbook’s Chapter 09, Lab 2. It has the exact commands.
- You need: a test tenant in the worldwide or GCC cloud, never production, with fictional people, licensed for Agent 365 (or Microsoft 365 E7) with Entra ID P1, and P2 for agent risk; and Chapters 01 and 02’s Lab 2 runs.
- What you do: read and set Users can create agent blueprints to No, make one governed agent with a sponsor who isn’t its owner and a managed identity as its credential, run the check with an app holding only the
agent-idpermissions, then dry-run and apply the plan as a person with Conditional Access Administrator. - Back-out: step 6, as a person in the admin center: delete the two policies, remove the attribute, disable then delete the test agent identity and blueprint, set the creation setting back if the tenant needs it, and delete the
outfolders. The code deletes nothing.
Knowledge check (5 minutes)
- Why is a client secret on a production blueprint a Finding?
- Secrets can’t be read in GCC
- The blueprint holds the credentials for every agent made from it, so one leaked secret is every agent’s
- Blueprints can’t hold certificates
- A secret makes the agent multitenant
- What does the code do in GCC High, where Microsoft has published no Agent ID availability?
- It fails
- It reads agent identities anyway
- It runs in Fallback mode: no agent identity read, and the same rules on the service principals the agents run as
- It skips every AI rule
- A sponsor leaves. When does the agent become orphaned?
- At once
- After 30 days
- When the new sponsor hasn’t confirmed within ten business days
- When its credential expires
- Why is inheritance set to
allAllowedfor application roles a Finding?- Any future grant to the blueprint becomes a grant to every agent
- It blocks token issuance
- It’s a preview feature
- It turns off Conditional Access
- What state does the agent Conditional Access write path create policies in?
- On
- Off
- Report-only, with Block as the only grant; it never turns a policy on
- Whatever the plan file says
- b. Taught in the guide’s AI agents section and the chapter’s Dangers.
- c. Taught in Decision D-NP6 as data.
- c. Taught in The program’s rules the code holds and The checks.
- a. Taught in The program’s rules the code holds.
- c. Taught in The agent Conditional Access plan and the separate write path.
Summary (2 minutes)
You checked eight fictional AI identities, filled the sponsor fields Chapter 02 recorded, found an orphaned agent and four P1 drift findings, ran the same rules in GCC High’s Fallback mode, and applied a report-only agent policy to a fake by its approved hash.
The evidence an ISSO keeps from this module: ai-identities.json, findings.json, orphan-candidates.json, agent-ca-plan.json with its approved SHA-256, and the dry-run and apply folders’ results.json and manifest.json, dated; the capability table for D-NP6; and the test result. Before shipping, work through the chapter’s Checklist.