Training module · Draft
Module 8: Convert Kerberos accounts to gMSAs and dMSAs
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. Gate G5 Conversion. Workbook Chapter 06. About 60 minutes.
Watch: the narrated deck for this module: Deck · ▶ Watch (about 16 minutes, captions) · MP4 · Script · Captions.
Also: the chapter’s narrated tour, about 11 minutes: ▶ Watch.
Introduction (3 minutes)
Some workloads can’t leave Kerberos yet. A traditional service account with an SPN and a password that never expires is the riskiest of them. A gMSA gives the same workload a password that Windows changes for it. A dMSA, on a Windows Server 2025 domain, supersedes the old account and disables its password.
But a gMSA works only while Active Directory is authoritative. So converting isn’t the end. Every gMSA and dMSA must either move to a workload identity, retire, or sit on a dated Kerberos island list with an end date and, where it has SPNs, an SPN owner. In this module you plan all three, and you see the write path refuse a plan nobody approved.
The federal requirement. The Kerberos accounts are the on-premises part of Gate G5. The guide’s Validate (G5 Conversion) step names IA-5, IA-9 and AU-2. The workbook chapter’s Federal requirements and evidence table has a row for every lab step.
| Requirement | KSI | Evidence |
|---|---|---|
| IA-5 Authenticator Management; CM-3 Configuration Change Control | KSI-IAM-SNU; KSI-CMT-LMC | kerberos-plan.json, with the register’s and each input’s SHA-256 |
| AC-2 Account Management; IA-5 | KSI-IAM-SNU | findings.json, NPI-KRB-01 to -07 and -09 |
| AC-6 Least Privilege; CM-5 Access Restrictions for Change | KSI-IAM-ELP, KSI-IAM-JIT (AC-6); KSI-CMT-RVP (CM-5) | Get-NpiKrbRequiredRight for the read and write tasks; password-retrievers.json |
| CM-8 System Component Inventory; AC-2 | KSI-PIY-GIV; KSI-IAM-SNU | island-list.csv; reconciliation.json |
| CM-3; AC-2(3) Disable Accounts | KSI-CMT-LMC; KSI-IAM-AAM | results.json and manifest.json in the apply 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:
- name the four dispositions a Kerberos account can have, and what each one needs;
- explain why a dMSA can’t be used for an MSA or an account with unconstrained delegation;
- read the plan, the island list and the reconciliation for a fictional domain;
- show that the write path runs only a plan whose SHA-256 was approved, and never deletes.
Prerequisites (1 minute)
Unit 1: The gate and the end date (8 minutes)
Read the guide’s Step 5: build the target, give it two owners, run old and new in parallel for 30 days, then disable, never delete. Then read:
- The guide’s Step 2, its prerequisites for a gMSA: one KDS root key for the forest, the wait of up to 10 hours, a name unique in the forest, and a password interval set only at creation.
- The guide’s Step 3, its step 7 on AD-side credentials.
- The guide’s Rollback row for Step 5.
- Optional, outside the timing: Service Accounts: From User Accounts to gMSAs to Microsoft Entra Workload Identities, Migrations A and B, the same moves by hand with Learn’s commands.
Unit 2: The plan and the island (12 minutes)
Read the workbook chapter in this order:
- The context the code depends on: what a gMSA and a dMSA are, why the chapter has an end date built in, and the program’s five rules (design guidance).
- The check and the plan: NPI-KRB-01 to -09, and one action per account.
- The island list and the reconciliation.
- The separate write path: why the read holds no domain administration and the write is a person’s.
Exercise: Lab 1, fixtures, no tenant (25 minutes)
This is the workbook’s Chapter 06, Lab 1, which explains each result. Its step 1 here makes the three runs Chapter 06 reads (Chapter 01’s register, Chapter 02’s owners and Chapter 03’s decisions), so the chapter’s steps 1 to 9 are steps 2 to 10 below. Work in examples/non-person-identity, in PowerShell 7. The domain is fictional, and -Apply changes only a fake, in memory.
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 the runs Chapter 06 reads
./Invoke-NpiInventory.ps1 -Cloud gcc -FixturePath tests/fixtures/estate.json -OutputPath out/inventory | Out-Null
./Invoke-NpiOwnership.ps1 -Cloud gcc -RegisterRun out/inventory -FixturePath tests/fixtures/estate.json -InputPath tests/fixtures/inputs -OutputPath out/ownership | Out-Null
./Invoke-NpiMatrix.ps1 -Cloud gcc -RegisterRun out/inventory -OwnershipRun out/ownership -InputPath tests/fixtures/ch03/inputs -FixturePath tests/fixtures/estate.json -SkuFixturePath tests/fixtures/ch03/subscribed-skus.json -OutputPath out/matrix | Out-Null
# Step 2: Run Learn's procedures against the stand-ins
Invoke-Pester -Path tests/KerberosIslandSamples.Tests.ps1 -Output None -PassThru |
Select-Object PassedCount, FailedCount, NotRunCount
# Step 3: List what each task runs
Import-Module ./powershell/NpiKerberosIsland/NpiKerberosIsland.psm1
foreach ($t in 'kerberos-island', 'kerberos-apply') { $r = Get-NpiKrbRequiredRight -Task $t; "$t : " + ($r.Cmdlets -join ', ') }
# Step 4: Check the Kerberos accounts and build the plan
./Invoke-NpiKerberosIsland.ps1 -Cloud gcc -RegisterRun out/inventory -MatrixRun out/matrix -ProgramDatesPath tests/fixtures/ch03/inputs/program-dates.json -FixturePath tests/fixtures/estate.json -KerberosFixturePath tests/fixtures/ch06/ad-kerberos.json -InputPath tests/fixtures/ch06/inputs -OutputPath out/kerberos
# Step 5: Read the findings
Get-Content out/kerberos/findings.json -Raw | ConvertFrom-Json | Sort-Object Rule | Format-Table Rule, Severity, ObjectKey
# Step 6: Read the plan
Get-Content out/kerberos/kerberos-plan.json -Raw | ConvertFrom-Json | Select-Object -ExpandProperty actions | Format-Table Action, Account, Write, Cmdlet
# Step 7: Read the island list and the reconciliation
Import-Csv out/kerberos/island-list.csv | Format-Table Account, Type, Disposition, SpnOwnerUpn, EndDate, AoAcceptedBy, ReadyForP46
Get-Content out/kerberos/reconciliation.json -Raw | ConvertFrom-Json | Format-Table Account, Type, Planned, Destination, Outcome
# Step 8: Dry-run the plan with its approved hash
$sha = (Get-FileHash out/kerberos/kerberos-plan.json -Algorithm SHA256).Hash.ToLower()
./Invoke-NpiKerberosIslandAction.ps1 -PlanPath out/kerberos/kerberos-plan.json -ApprovedSha256 $sha -FixturePath tests/fixtures/estate.json -KerberosFixturePath tests/fixtures/ch06/ad-kerberos.json -OutputPath out/kerberos-dryrun
# Step 9: Apply it to the fake, then try a hash nobody approved
./Invoke-NpiKerberosIslandAction.ps1 -PlanPath out/kerberos/kerberos-plan.json -ApprovedSha256 $sha -FixturePath tests/fixtures/estate.json -KerberosFixturePath tests/fixtures/ch06/ad-kerberos.json -OutputPath out/kerberos-apply -Apply
./Invoke-NpiKerberosIslandAction.ps1 -PlanPath out/kerberos/kerberos-plan.json -ApprovedSha256 ('0' * 64) -FixturePath tests/fixtures/estate.json -KerberosFixturePath tests/fixtures/ch06/ad-kerberos.json -OutputPath out/kerberos-x
# Step 10: Run the tests
Invoke-Pester -Path tests
# After the steps
Remove-Item -Recurse -Force outMake the runs Chapter 06 reads:
./Invoke-NpiInventory.ps1 -Cloud gcc -FixturePath tests/fixtures/estate.json -OutputPath out/inventory | Out-Null ./Invoke-NpiOwnership.ps1 -Cloud gcc -RegisterRun out/inventory -FixturePath tests/fixtures/estate.json -InputPath tests/fixtures/inputs -OutputPath out/ownership | Out-Null ./Invoke-NpiMatrix.ps1 -Cloud gcc -RegisterRun out/inventory -OwnershipRun out/ownership -InputPath tests/fixtures/ch03/inputs -FixturePath tests/fixtures/estate.json -SkuFixturePath tests/fixtures/ch03/subscribed-skus.json -OutputPath out/matrix | Out-NullIt prints nothing. The three folders hold the register, the owners and the owners’ D-NP3 decisions, which Chapter 06 reads instead of a second list.
Run Learn’s procedures against the stand-ins:
Invoke-Pester -Path tests/KerberosIslandSamples.Tests.ps1 -Output None -PassThru | Select-Object PassedCount, FailedCount, NotRunCountExpect
PassedCount 17,FailedCount 0,NotRunCount 0.List what each task runs:
Import-Module ./powershell/NpiKerberosIsland/NpiKerberosIsland.psm1 foreach ($t in 'kerberos-island', 'kerberos-apply') { $r = Get-NpiKrbRequiredRight -Task $t; "$t : " + ($r.Cmdlets -join ', ') }kerberos-island : Get-ADComputer, Get-ADDomainController, Get-ADObject, Get-ADServiceAccount, Get-ADUser kerberos-apply : Complete-ADServiceAccountMigration, Disable-ADAccount, Get-ADComputer, Get-ADDomainController, Get-ADObject, Get-ADServiceAccount, Get-ADUser, New-ADServiceAccount, Set-ADServiceAccount, Start-ADServiceAccountMigrationThe read task runs only
Get-AD*cmdlets. No cmdlet that removes or undoes is on either list.Check the Kerberos accounts and build the plan:
./Invoke-NpiKerberosIsland.ps1 -Cloud gcc -RegisterRun out/inventory -MatrixRun out/matrix -ProgramDatesPath tests/fixtures/ch03/inputs/program-dates.json -FixturePath tests/fixtures/estate.json -KerberosFixturePath tests/fixtures/ch06/ad-kerberos.json -InputPath tests/fixtures/ch06/inputs -OutputPath out/kerberosKerberos accounts: 9 in scope; KDS root key Ready Findings: 5 Finding, 1 Warn, 2 Info Plan: 9 actions, 4 of them writes (1 CompleteDmsaMigration, 2 CreateGmsa, 1 SetRetrievers); nothing sent Island list: 6 accounts, 4 ready for P4.6; hand-off ready: False Reconciliation (D11 KerberosIsland): 1 HandedOff, 4 OnIslandList, 1 Unreconciled Plan SHA-256: e8fb8355ee0989d9ce9d7b107cd52f45d0eea5f155a49c1667a1bf5840de9cca-MatrixRunis Chapter 03’s run, where the dispositions come from;-ProgramDatesPathis the one program-dates file, which the run refuses unless its SHA-256 is the one Chapter 03’s run recorded. The plan’s hash is the same on every run with these inputs.Read the findings:
Get-Content out/kerberos/findings.json -Raw | ConvertFrom-Json | Sort-Object Rule | Format-Table Rule, Severity, ObjectKeyRule Severity ObjectKey ---- -------- --------- NPI-KRB-03 Finding gmsa-web$ NPI-KRB-05 Finding svc-intranet NPI-KRB-05 Finding svc-webfront NPI-KRB-06 Finding svc-intranet NPI-KRB-07 Finding gmsa-intranet$ NPI-KRB-08 Warn gmsa-ops$ NPI-KRB-09 Info gmsa-sql$ NPI-KRB-09 Info svc-sqlprodsvc-webfrontis trusted for unconstrained delegation, which stops working once a dMSA migration completes, so in Chapter 03 its owner kept it on Kerberos (KeepKerberos) with the authorizing official’s acceptance; NPI-KRB-05 flags it because it has no SPN owner. NPI-KRB-05 and -06 flagsvc-intranet: Chapter 03 re-platforms it to a gMSA, but it has no SPN owner and its end date (2028) is after program G5. The kept gMSAsgmsa-ops$andgmsa-web$end August 31, 2027, after D08 but before program G5, which is their bound, as in Chapter 03;gmsa-ops$has no SPNs, so it needs no SPN owner. NPI-KRB-08 warns thatgmsa-ops$is in Domain Admins.Read the plan:
Get-Content out/kerberos/kerberos-plan.json -Raw | ConvertFrom-Json | Select-Object -ExpandProperty actions | Format-Table Action, Account, Write, CmdletAction Account Write Cmdlet ------ ------- ----- ------ Island gmsa-ops$ False HandedOff gmsa-sql$ False Island gmsa-web$ False SetRetrievers gmsa-web$ True Set-ADServiceAccount CreateGmsa msa-print01$ True New-ADServiceAccount CompleteDmsaMigration svc-hrsync True Complete-ADServiceAccountMigration CreateGmsa svc-intranet True New-ADServiceAccount HandedOff svc-sqlprod False Island svc-webfront FalseFour writes:
SetRetrieversforgmsa-web$,CreateGmsaformsa-print01$andsvc-intranet, andCompleteDmsaMigrationforsvc-hrsync, whose migration started 40 days ago.gmsa-ops$,gmsa-web$andsvc-webfrontstay on the island, as Chapter 03 decided.Read the island list and the reconciliation:
Import-Csv out/kerberos/island-list.csv | Format-Table Account, Type, Disposition, SpnOwnerUpn, EndDate, AoAcceptedBy, ReadyForP46 Get-Content out/kerberos/reconciliation.json -Raw | ConvertFrom-Json | Format-Table Account, Type, Planned, Destination, OutcomeAccount Type Disposition SpnOwnerUpn EndDate AoAcceptedBy ReadyForP46 ------- ---- ----------- ----------- ------- ------------ ----------- dmsa-hrsync$ dMSA Replatform hr-apps.owner@contoso.example 2027-06-30 True gmsa-intranet$ gMSA Replatform 2028-12-31 False gmsa-ops$ gMSA Keep 2027-08-31 True gmsa-print$ gMSA Replatform print.owner@contoso.example 2027-06-30 True gmsa-web$ gMSA Keep erin@contoso.example 2027-08-31 True svc-webfront Traditional KeepKerberos 2027-05-31 ao@contoso.example False Account Type Planned Destination Outcome ------- ---- ------- ----------- ------- dmsa-hrsync$ dMSA False Island OnIslandList gmsa-ops$ gMSA False Island OnIslandList gmsa-sql$ gMSA False HandedOff HandedOff gmsa-web$ gMSA False Island OnIslandList gmsa-intranet$ gMSA True Island Unreconciled gmsa-print$ gMSA True Island OnIslandListSix island rows, four ready for P4.6.
gmsa-intranet$has no SPN owner and ends2028-12-31, so it isn’t ready, and its reconciliation outcome isUnreconciled.svc-webfrontcarries the authorizing official’s acceptance from Chapter 03, so it becomes ready once it has an SPN owner.Dry-run the plan with its approved hash:
$sha = (Get-FileHash out/kerberos/kerberos-plan.json -Algorithm SHA256).Hash.ToLower() ./Invoke-NpiKerberosIslandAction.ps1 -PlanPath out/kerberos/kerberos-plan.json -ApprovedSha256 $sha -FixturePath tests/fixtures/estate.json -KerberosFixturePath tests/fixtures/ch06/ad-kerberos.json -OutputPath out/kerberos-dryrunDryRun: 4 WouldSend WouldSend SetRetrievers gmsa-web$ Set-ADServiceAccount WouldSend CreateGmsa msa-print01$ New-ADServiceAccount WouldSend CompleteDmsaMigration svc-hrsync Complete-ADServiceAccountMigration WouldSend CreateGmsa svc-intranet New-ADServiceAccountApply it to the fake, then try a hash nobody approved:
./Invoke-NpiKerberosIslandAction.ps1 -PlanPath out/kerberos/kerberos-plan.json -ApprovedSha256 $sha -FixturePath tests/fixtures/estate.json -KerberosFixturePath tests/fixtures/ch06/ad-kerberos.json -OutputPath out/kerberos-apply -Apply ./Invoke-NpiKerberosIslandAction.ps1 -PlanPath out/kerberos/kerberos-plan.json -ApprovedSha256 ('0' * 64) -FixturePath tests/fixtures/estate.json -KerberosFixturePath tests/fixtures/ch06/ad-kerberos.json -OutputPath out/kerberos-xExpect
Apply: 4 Sent. Then the refusal:The plan's SHA-256 is e8fb8355…, 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 domain (5 minutes to read)
Not run by the authors. Follow the workbook’s Chapter 06, Lab 2. It has the exact commands.
- You need: a test Active Directory domain, never production, with at least one Windows Server 2025 domain controller, a Windows Server 2025 member server, an existing KDS root key, and Chapter 01’s Lab 2 run.
- What you do: make a traditional test account and a test MSA, grant the read only
msKds-UseStartTimeandmsKds-CreateTime, run the check, then apply the plan as a person with Domain Admins or equivalent rights, start the dMSA migration, and complete it after the 30-day parallel run. - Back-out: step 6 runs Learn’s
Undo-ADServiceAccountMigrationby hand (the code never runs it), re-enables the test account, removes the test objects by hand, and deletes theoutfolders.
Knowledge check (5 minutes)
- An account is kept on Kerberos (
KeepKerberos). What must it have?- A client secret in Key Vault
- The authorizing official’s acceptance, an SPN owner and an end date no later than D08’s
- A Conditional Access policy
- Nothing, until G5
- Why would this chapter block a dMSA for
svc-webfront, the reason its owner kept it on Kerberos?- It’s a SQL Server account
- It’s trusted for unconstrained delegation, which stops working once a dMSA migration completes
- Its domain has no KDS root key
- It already has a gMSA
- What does the write path do to the old account when a dMSA migration completes?
- Deletes it
- Moves it to another domain
- Nothing:
Complete-ADServiceAccountMigrationdisables it and moves its SPNs to the dMSA; no delete cmdlet exists in the code - Resets its password
- You pass a hash of zeros to
Invoke-NpiKerberosIslandAction.ps1. What happens?- It runs in dry-run mode
- It refuses, because the plan’s SHA-256 isn’t the approved one
- It applies the plan and logs a warning
- It asks for confirmation
- Why does the read task hold no domain administration?
- It reads only the KDS root key’s start and create times, with a delegated read, plus the default read
- Domain administration isn’t available in GCC
- The read runs as a gMSA
- Learn forbids it
- b. Chapter 03’s rule for an account kept on Kerberos; a kept gMSA or dMSA ends by program G5 instead, and needs an SPN owner only if it has SPNs. Taught in The context the code depends on.
- b. NPI-KRB-04. Taught in The check and the plan and the chapter’s Lab 1.
- c. Never delete. Taught in The check and the plan and the chapter’s Dangers.
- b. Taught in The separate write path.
- a. Taught in The separate write path.
Summary (2 minutes)
You checked nine Kerberos accounts in a fictional domain, read a plan with four writes, an island list and a reconciliation, applied the plan to a fake by its hash, and saw a plan nobody approved refused.
The evidence an ISSO keeps for G5 from this module: kerberos-plan.json with its approved SHA-256, findings.json, island-list.csv, reconciliation.json, and the apply folder’s results.json and manifest.json, dated; and the test result. Before shipping, work through the chapter’s Checklist.