Training module · Draft
Module 7: Convert to managed identities and federation
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 Non-Person Identity learning path. Gate G5 Conversion. Workbook Chapter 05. About 60 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)
A federated credential tells Microsoft Entra ID to trust tokens from an outside issuer, such as GitHub Actions or a Kubernetes cluster. Three values must match the workload’s token exactly, case for case: the issuer, the subject and the audience. And a credential with the wrong subject is created without any error. It fails only when the workload tries to exchange its token.
In this module you plan conversions in the program’s order: a managed identity for an Azure-hosted workload, a federated credential where the host has an issuer, and a certificate only where neither applies. Each federated credential is checked against the claims the workload will present and tested against a fake token endpoint before it is created. Then the old and new credentials run side by side for 30 days, and the legacy account is disabled, never deleted.
The federal requirement. Gate G5 supports NIST SP 800-53 IA-5, IA-9 and AU-2. 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 guide’s Validate (G5 Conversion) step names them, and the workbook chapter’s Federal requirements and evidence table has a row for every lab step.
| Requirement | KSI | Evidence |
|---|---|---|
| IA-5 and IA-5(2) Authenticator Management; IA-9 Service Identification and Authentication | KSI-IAM-SNU (IA-5); none cites IA-9 | conversions.json; plan.json; findings.json, NPI-WIF-01 to -09 |
| IA-9; CM-4(2) Verification of Controls | KSI-CMT-VTD, KSI-CMT-LMC (CM-4(2)) | exchange-tests.json |
| AC-2 Account Management; AC-2(3) Disable Accounts | KSI-IAM-SNU (AC-2); KSI-IAM-AAM (AC-2(3)) | conversion-records.json; cutover-log.csv; the apply folder’s results.json |
| AC-6 Least Privilege; CM-5 Access Restrictions for Change | KSI-IAM-ELP, KSI-IAM-JIT (AC-6); KSI-CMT-RVP (CM-5) | The permissions for federation and federation-apply |
| CM-3 Configuration Change Control | KSI-CMT-RMV, KSI-CMT-LMC | The plan’s SHA-256; the dry-run and apply folders |
| AU-2 and AU-12, the new identity’s sign-ins | KSI-MLA-LET | Lab 2: the beta sign-in log entry with clientCredentialType federatedIdentityCredential |
| AU-9 and SI-7, the run’s integrity | KSI-MLA-OSM (AU-9); KSI-SVC-VRI (SI-7) | manifest.json in each output folder |
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:
- state the order of targets, and the finding a target out of order raises;
- name the three values a federated credential must match, and the audience Entra ID expects;
- explain why every federated credential gets a token exchange test before it’s created;
- read a conversion’s cutover gate and parallel run status, and say when the legacy account may be disabled.
Prerequisites (1 minute)
Unit 1: What the gate asks for (7 minutes)
Read the guide’s Step 5: build the target, give it two human owners, run old and new for 30 days, disable the legacy account, and record the result. Then read the guide’s Step 2 decision table again, and the Dangers row “Federation trusts too much”.
Unit 2: What a federated credential must match (10 minutes)
Read the workbook chapter in this order:
- What the code depends on: the order, the three values, the limit of 20, and why a wrong subject is created without error.
- The checks: NPI-WIF-01 to -09, what the workload presents, and the token exchange test.
Unit 3: The plan, the parallel run and the write path (8 minutes)
- The plan: the actions, and the evidence for a clean run (design guidance for the thresholds).
- The separate write path: the approved hash, one write at a time, never a
DELETE, and a person with Cloud Application Administrator and Managed Identity Contributor.
Exercise: Lab 1, fixtures, no tenant (20 minutes)
This is the workbook’s Chapter 05, Lab 1, which explains each result. Work in examples/non-person-identity, in PowerShell 7. The tenant is fictional.
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: Send Microsoft's samples to the fakes
Invoke-Pester -Path tests/NpiFederationSamples.Tests.ps1 -Output None -PassThru |
Select-Object PassedCount, FailedCount, NotRunCount
# Step 2: List what each task needs
Import-Module ./powershell/NpiFederation/NpiFederation.psm1
foreach ($t in 'federation', 'federation-apply') { $p = Get-NpiWifRequiredPermission -Task $t; "$t : " + ($p.GraphPermissions -join ', ') + ' | ' + ($p.AzureRoles -join ', ') }
# Step 3: Build the plan
./Invoke-NpiFederation.ps1 -Cloud gcc -ConversionsPath tests/fixtures/ch05/conversions.json -CutoverLogPath tests/fixtures/ch05/cutover-log.csv -FixturePath tests/fixtures/ch05/tenant.json -OutputPath out/federation
# Step 4: Read the findings
Get-Content out/federation/findings.json -Raw | ConvertFrom-Json | Format-Table Rule, Severity, ObjectKey
# Step 5: Read the exchange tests
Get-Content out/federation/exchange-tests.json -Raw | ConvertFrom-Json | Format-Table Credential, SubjectSource, Passed
# Step 6: Read the conversions
Get-Content out/federation/conversion-records.json -Raw | ConvertFrom-Json | ForEach-Object { [pscustomobject]@{ Id = $_.ConversionId; Target = $_.TargetKind; Gate = $_.CutoverGate; Run = $_.ParallelRun.Status; Day = $_.ParallelRun.Day } } | Format-Table
# Step 7: Dry-run the plan with its approved hash
$sha = (Get-FileHash out/federation/plan.json -Algorithm SHA256).Hash.ToLower()
./Invoke-NpiFederation.ps1 -Cloud gcc -PlanPath out/federation/plan.json -ApprovedSha256 $sha -FixturePath tests/fixtures/ch05/tenant.json -OutputPath out/federation-dryrun
# Step 8: Apply it to the fake, then try a hash nobody approved
./Invoke-NpiFederation.ps1 -Cloud gcc -PlanPath out/federation/plan.json -ApprovedSha256 $sha -FixturePath tests/fixtures/ch05/tenant.json -OutputPath out/federation-apply -Apply
./Invoke-NpiFederation.ps1 -Cloud gcc -PlanPath out/federation/plan.json -ApprovedSha256 ('0' * 64) -FixturePath tests/fixtures/ch05/tenant.json -OutputPath out/federation-x
# Step 9: Run the tests
Invoke-Pester -Path tests
# After the steps
Remove-Item -Recurse -Force outSend Microsoft’s samples to the fakes:
Invoke-Pester -Path tests/NpiFederationSamples.Tests.ps1 -Output None -PassThru | Select-Object PassedCount, FailedCount, NotRunCountExpect
PassedCount 17,FailedCount 0,NotRunCount 0. One test creates a credential from the Graph sample, then exchanges a token with another subject: the create succeeds and the exchange fails withAADSTS70021.List what each task needs:
Import-Module ./powershell/NpiFederation/NpiFederation.psm1 foreach ($t in 'federation', 'federation-apply') { $p = Get-NpiWifRequiredPermission -Task $t; "$t : " + ($p.GraphPermissions -join ', ') + ' | ' + ($p.AzureRoles -join ', ') }federation : Application.Read.All, AuditLog.Read.All | Reader federation-apply : Application.Read.All, Application.ReadWrite.All | Managed Identity Contributor, ReaderBuild the plan:
./Invoke-NpiFederation.ps1 -Cloud gcc -ConversionsPath tests/fixtures/ch05/conversions.json -CutoverLogPath tests/fixtures/ch05/cutover-log.csv -FixturePath tests/fixtures/ch05/tenant.json -OutputPath out/federationConversions: 8 in 2 wave(s); token exchange tests: 4 passed, 2 failed Findings: 7 Finding, 1 Warn Plan: 20 actions, 4 of them writes (2 CreateCredential, 1 CreateIdentity, 1 DisableLegacy); nothing sentIt also prints each conversion’s parallel run status and the plan’s SHA-256,
5ec0a52c70dc49ac6904c6923ac9b292e3c312d38594b6210cc9178c9f188bf2. The hash is the same on every run with these inputs.Read the findings:
Get-Content out/federation/findings.json -Raw | ConvertFrom-Json | Format-Table Rule, Severity, ObjectKeyExpect eight rows.
payroll-prodhas an issuer with a trailing slash and the environmentProductionwhere the workflow’s isproduction: NPI-WIF-03, NPI-WIF-02 and a failed exchange, NPI-WIF-08.github-intranet-taghas an audience with a leading space: NPI-WIF-04 and NPI-WIF-08.conv-06gives an Azure Functions app a certificate when a managed identity is available: NPI-WIF-01.conv-08’s secret still signs in after cutover: NPI-WIF-09.Read the exchange tests:
Get-Content out/federation/exchange-tests.json -Raw | ConvertFrom-Json | Format-Table Credential, SubjectSource, PassedExpect six tests:
payroll-prodandgithub-intranet-tagfail, and the other four pass.Read the conversions:
Get-Content out/federation/conversion-records.json -Raw | ConvertFrom-Json | ForEach-Object { [pscustomobject]@{ Id = $_.ConversionId; Target = $_.TargetKind; Gate = $_.CutoverGate; Run = $_.ParallelRun.Status; Day = $_.ParallelRun.Day } } | Format-TableExpect
conv-01ReadyToDisableLegacyon day 40,conv-07ParallelRunningon day 15 andconv-08LegacyStillInUseon day 59. The other five areAwaitingCutover.Dry-run the plan with its approved hash:
$sha = (Get-FileHash out/federation/plan.json -Algorithm SHA256).Hash.ToLower() ./Invoke-NpiFederation.ps1 -Cloud gcc -PlanPath out/federation/plan.json -ApprovedSha256 $sha -FixturePath tests/fixtures/ch05/tenant.json -OutputPath out/federation-dryrunExpect
DryRun: 4 WouldSend:Disable-ADAccountforconv-01’s legacy account, onePOSTof an application’s federated credential, then aPUTof the identityid-webfrontbefore aPUTof its credential.Apply it to the fake, then try a hash nobody approved:
./Invoke-NpiFederation.ps1 -Cloud gcc -PlanPath out/federation/plan.json -ApprovedSha256 $sha -FixturePath tests/fixtures/ch05/tenant.json -OutputPath out/federation-apply -Apply ./Invoke-NpiFederation.ps1 -Cloud gcc -PlanPath out/federation/plan.json -ApprovedSha256 ('0' * 64) -FixturePath tests/fixtures/ch05/tenant.json -OutputPath out/federation-xExpect
Apply: 4 Sent, in the same order, then the refusal:The plan's SHA-256 is 5ec0a52c…, 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 05, Lab 2. It has the exact commands.
- You need: a test tenant in the worldwide or GCC cloud, a test subscription, a test Active Directory domain and a test repository, never production, with fictional names; a reader app with
Application.Read.All,AuditLog.Read.Alland Reader; and a person with Cloud Application Administrator and Managed Identity Contributor for the write. - What you do: make a test conversion from a client secret to a federated credential, capture the real
issandsubfrom one workflow run, plan, apply, exchange for real, and run the parallel run through to the legacy account’s disable. - Back-out: step 7 enables the test account again, removes the test credentials, deletes the test identity, the test app registration and the test account by hand (this code never deletes), and deletes the
outfolders.
Knowledge check (5 minutes)
- A credential’s subject has
Productionwhere the workflow presentsproduction. What happens when it’s created in Entra ID?- Creation fails with an error
- It’s created without error, and the token exchange fails later; the check flags it first (NPI-WIF-02)
- Entra ID corrects the case
- It works, because matching ignores case
- What audience does Entra ID expect on a federated credential?
- Any value the workload sends
- Exactly one audience,
api://AzureADTokenExchange https://graph.microsoft.com- The app’s client ID
- An Azure Functions app is given a certificate. Why is that a finding?
- Certificates are banned
- The plan’s order puts an Azure-hosted workload on a managed identity (NPI-WIF-01)
- Functions can’t use certificates
- The certificate is too long-lived
conv-01ran 40 days with no logon by the old account, and the owner approved. What does the plan do with the legacy account?- Deletes it
- Disables it with
Disable-ADAccount, after review; it’s never deleted by this code - Leaves it enabled for another 30 days
- Moves it to another domain
- What does the token exchange test in Lab 1 prove?
- That the workload’s issuer will present those claims
- That a credential will match the claims you recorded, against a fake that matches as Learn describes; the real issuer’s answer is Lab 2’s
- That the token’s signature is valid
- That the credential has propagated
- b. Learn says a wrong subject is created successfully and fails only at the exchange. Taught in What the code depends on and The checks.
- b. Taught in What the code depends on and the guide’s Step 3, item 8.
- b. Taught in The checks and the guide’s Step 2.
- b. Disable, never delete. Taught in The plan and the guide’s Step 5.
- b. Taught in The checks and the chapter’s illustrative code note above Lab 1.
Summary (2 minutes)
You planned eight conversions in two waves, caught a trailing slash, a wrong case and a stray space before any credential reached the tenant, read each conversion’s parallel run, and applied four approved writes to the fake, an identity before its credential. The write path refused a plan nobody reviewed.
The evidence an ISSO keeps for G5 from this module: conversions.json, cutover-log.csv, plan.json with its approved SHA-256, findings.json, exchange-tests.json, conversion-records.json, and the apply folder’s results.json and manifest.json, dated. Before shipping, work through the chapter’s Checklist.
Next: Module 8: Convert Kerberos accounts to gMSAs and dMSAs.