The migration project closed six months ago. You moved the fleet off the old standalone Microsoft LAPS (the AdmPwd MSI everyone installed in 2019) and onto modern, in-box Windows LAPS, backing passwords up to Microsoft Entra ID through an Intune policy. Every dashboard you look at says you are done: the Intune configuration profile reports success, the Entra device blade shows a local admin password on record, and nobody has raised a ticket. Then an engineer needs the local admin password for a machine that will not reach the network, reads it out of Entra, types it at the recovery console, and it is wrong. The account exists. The password on that device is something else entirely, and it is sitting in an attribute nobody thought was still being written.
This is migration drift, and it is one of the quietest failure modes in the whole LAPS story because every central surface you would normally trust keeps reporting green. The problem is not in the cloud. It is on the device, where a legacy client that was never uninstalled and a legacy Group Policy that was never unlinked are still doing their old job in parallel with the new one. This post walks through why two password managers can end up running on the same account at once, how to prove which one is actually winning on a given device, and hands you a read-only PowerShell script that flags the drift before an engineer discovers it the hard way at a recovery screen.
Get-LapsMigrationDrift.ps1 is free and open source. It reads local registry and event-log state only — no Graph, no Entra, no AD authentication. Download it from the Imran76Awan/Daily-Tasks repository on GitHub.
The problem — the portal shows a password, but it is not the password on the device
Windows LAPS and legacy Microsoft LAPS are two entirely separate products that happen to solve the same problem. Legacy Microsoft LAPS is the old downloadable solution: an MSI that installs a Group Policy Client Side Extension (AdmPwd.dll), reads a legacy GPO, and writes the local administrator password as clear text into the ms-Mcs-AdmPwd attribute on the computer object in on-premises Active Directory. Windows LAPS is the modern replacement built directly into Windows, managed through an Intune CSP or a new Group Policy, and capable of backing the password up to either Active Directory or Microsoft Entra ID.
The trap is that installing Windows LAPS does not remove legacy Microsoft LAPS, and applying a Windows LAPS policy does not automatically stop a legacy GPO from applying. If a device still has the legacy CSE registered and still receives the legacy GPO — because the migration removed the policy from one OU but not another, or because the MSI was left in the base image, or because a device simply had not checked in against a domain controller when the cut-over happened — then it now has two independent agents that both believe they own the built-in Administrator account. They rotate on different schedules. They write to different places. And critically, only one of them corresponds to the password your help desk reads out of the portal.
So the device shown below is not misbehaving. It is doing exactly what it was told, twice. Windows LAPS rotated the password and escrowed it to Entra ID. The old AdmPwd client, still installed, rotated it again later and wrote a different value to ms-Mcs-AdmPwd in AD. Whichever one wrote last is what is actually set on the account. The Entra copy your engineer trusts can be hours or days out of date, and nothing in the Intune or Entra UI will tell them that.
Why it happens — two policy engines, one account, and a precedence order that only covers half of it
To understand the drift you have to separate two things that sound identical but behave very differently: the in-box Windows LAPS component honouring a legacy policy, versus the old legacy product still being physically installed.
Windows LAPS reads policy from a set of distinct registry roots, and it evaluates them in a fixed order. Microsoft documents the roots and the rule plainly: Windows LAPS queries each known policy root from the top down, and the first root that has any explicitly defined setting becomes the active policy. Settings are never merged across roots. The order is:
| Precedence | Policy source | Registry root |
|---|---|---|
| 1 (highest) | LAPS CSP (Intune) | HKLM\Software\Microsoft\Policies\LAPS |
| 2 | LAPS Group Policy | HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\LAPS |
| 3 | LAPS Local Configuration | HKLM\Software\Microsoft\Windows\CurrentVersion\LAPS\Config |
| 4 (lowest) | Legacy Microsoft LAPS | HKLM\Software\Policies\Microsoft Services\AdmPwd |
The single most important setting under any of the modern roots is BackupDirectory, and it takes exactly three values: 0 means disabled (the password is not backed up), 1 means back up to Microsoft Entra ID only, and 2 means back up to Windows Server Active Directory only. When Windows LAPS finds a legacy policy root but no modern one, it runs in what Microsoft calls legacy Microsoft LAPS emulation mode — it honours the old GPO and, exactly like the old product, writes clear text to ms-Mcs-AdmPwd in AD. Emulation mode cannot back up to Entra and cannot encrypt.
Here is the crucial gap. That precedence order resolves conflicts within Windows LAPS's own view of the world. If a native Windows LAPS policy is present, it always wins over emulation, and the legacy policy is ignored. But the precedence order says nothing about the legacy product being separately installed as its own CSE. The old AdmPwd.dll is a different code path entirely. It does not consult the Windows LAPS registry roots, it does not care about BackupDirectory, and it does not defer to anything. If that DLL is registered and a legacy GPO reaches it, it rotates the account on its own, independently of whatever Windows LAPS is doing to the same account. That is the true two-managers state, and it is exactly the arrangement Microsoft warns is unsupported.
BackupDirectory=1Rotates + escrows to
Microsoft Entra ID
Rotates + writes clear text to
ms-Mcs-AdmPwd in AD
The registry makes the collision visible if you know exactly where to look. The panel below is what a drifted device actually shows: a modern CSP policy pointing at Entra, sitting right alongside the legacy CSE registration and the legacy policy root that should both have been removed at cut-over.
{D76B9641-3288-4f75-942D-087DE603E3EA} is how Windows itself detects whether legacy Microsoft LAPS is installed — it is the legacy CSE's GPExtensions registration. Microsoft's own emulation-mode logic checks the DllName value under exactly that key. If DllName is present and points at a file, Windows considers legacy LAPS installed and will refuse to enter emulation mode. That same check is the most reliable device-side signal that the old client was never cleanly removed, which is why the script below keys off it rather than guessing.How to verify — ask the event log what the device actually did, not what it was told
Before trusting any script across a fleet, confirm the mechanism on one device you can inspect directly. The configuration in the registry only tells you what the device was asked to do. What you need is evidence of what it actually did, and Windows LAPS records precisely that in its own dedicated event channel: Applications and Services Logs > Microsoft > Windows > LAPS > Operational, whose full channel name is Microsoft-Windows-LAPS/Operational.
Three event IDs answer the entire question. Each background policy cycle logs a configuration event that names the policy source and target directory: 10021 means it processed a policy backing up to Active Directory, 10022 means Microsoft Entra ID, and 10023 means it is running in legacy Microsoft LAPS emulation mode. Then the success events tell you the payload actually landed: 10018 is logged on a successful backup to Active Directory, and 10029 on a successful backup to Microsoft Entra ID. If your registry says BackupDirectory=1 (Entra) but the newest confirmation event is a 10018, or your most recent config event is a 10023, the device is drifting.
Get-LapsADPassword or Get-LapsAADPassword. Both are real, supported cmdlets, but both exist to reveal the actual password value and require the corresponding read privilege. A drift audit does not need the password — it needs to know which directory the device wrote to and when. The event log answers that without exposing a single credential. If you want a deeper local trace, Get-LapsDiagnostics collects LAPS logs and tracing from the machine without disclosing the password either, though note it writes a diagnostics bundle to disk, which is why the audit script deliberately does not call it.The fix — a read-only drift audit you can run standalone or as a Proactive Remediation
Get-LapsMigrationDrift.ps1 automates the exact manual check above and combines it with the registry footprint check, on a single device, in one pass. It is strictly read-only: it never creates, modifies, enables, disables, resets, rotates, or removes anything. It reads three things and compares them — the configured intent (the effective BackupDirectory across the modern policy roots, in precedence order), the legacy footprint (the CSE registration, the legacy MSI product code, and the legacy policy root), and the observed reality (the newest 10021/10022/10023 config event and the newest 10018/10029 success event). Where intent and reality disagree, or where a legacy manager is present alongside a modern policy, it raises a finding.
It supports two exit-code schemes. Run standalone, it uses 0 for clean, 1 for a genuine script or query error, and 2 for drift detected, so you can tell a real failure apart from a real finding. Add -ProactiveRemediation and it collapses to the scheme Intune Proactive Remediations expect for a detection script: 0 for compliant and 1 for issue-found (an error also maps to 1, so a broken run never masquerades as compliant). The script also fails loud — any unexpected error while reading state sets an internal flag, prints a clear failure, and forces a non-zero exit, so it never prints "clean" off the back of a read that actually threw.
That output tells a complete story in one screen. The device was configured for Entra, the CSP policy is genuinely active, but the newest config event is a 10023 and the newest success event is a 10018 — the legacy client rotated last and wrote to Active Directory, so the value in Entra is stale. The two AMBER findings name the exact artefacts to remove to end the fight: the MSI product and the legacy GPO. Deploying this as a Proactive Remediation detection script (no remediation script attached) turns it into a fleet-wide census of which devices never actually finished the migration.
- Detection script: upload
Get-LapsMigrationDrift.ps1with the-ProactiveRemediationswitch invoked (wrap it, or set the switch default in the script for your package). - Remediation script: none — leave this detection-only, per the green callout above.
- Run this script using the logged-on credentials: No (SYSTEM reads registry and the event log fine).
- Run script in 64-bit PowerShell: Yes.
- Schedule: Daily. Migration drift does not appear hourly, and the read footprint is negligible.
Proof it worked — re-run after removing the legacy client, and watch the two managers become one
The remediation is not something this script does for you; it is a deliberate manual sequence. Following Microsoft's migration guidance, you unlink the legacy GPO so the old policy stops applying, then remove the legacy software from the device — either by uninstalling the MSI (msiexec.exe /q /uninstall {97E2CA7B-B657-4FF7-A6DB-30ECC73E1E28}) or, if it was hand-registered, by unregistering AdmPwd.dll. Once the legacy CSE is gone and only the Windows LAPS CSP policy remains, the next policy cycle has a single owner again. Re-running the audit is how you confirm it, rather than assuming.
Now the config event is a 10022 and the success event is a 10029: policy source CSP, directory Entra, agreement between what the device was told and what it did. The legacy footprint is gone, and the password an engineer reads from Entra is the password that is actually on the account. That is the whole point of the audit — not to prove a device is encrypted-and-configured, which every portal already claims, but to prove that only one manager is rotating the credential and that it is the one your recovery process trusts.
I will run this audit across your fleet, find every device still running the legacy client alongside Windows LAPS, and help you retire the old solution cleanly without ever locking anyone out of a local admin account.
References
- Configure policy settings for Windows LAPS (policy roots, precedence, and BackupDirectory values) — Microsoft Learn
- Get started with Windows LAPS in legacy Microsoft LAPS emulation mode (CSE detection GUID, ms-Mcs-AdmPwd) — Microsoft Learn
- Use Windows LAPS event logs (Operational channel and events 10018/10021/10022/10023/10029) — Microsoft Learn
- Prepare for Windows LAPS deployment and migration (side-by-side rules and removing the legacy MSI) — Microsoft Learn
- LAPS PowerShell module reference (Get-LapsDiagnostics, Get-LapsADPassword, Get-LapsAADPassword) — Microsoft Learn
Get-LapsMigrationDrift.ps1 is a read-only detection script — it never modifies LAPS, the registry, or the account it audits. Safe to deploy fleet-wide as a detection-only Proactive Remediation while you work through the legacy-removal backlog.