Le LAPS de mon infra perso tournait depuis des années sur le vieux MSI Microsoft : une GPO pour le déployer, une autre pour le configurer. Jusqu’au jour où une station passée en Windows 11 24H2 s’est mise à réclamer un redémarrage à chaque gpupdate. Le MSI legacy n’avait plus sa place dans le parc. Voici la migration vers Windows LAPS, intégré à Windows depuis avril 2023, avec surtout les pièges qui m’ont coûté du temps.
L’essentiel en 30 secondes
- Le LAPS legacy (MSI de 2016) est déprécié depuis Windows 11 23H2, et son installation y est bloquée.
- Windows LAPS est intégré à Windows 10/11 et Server 2019+ : on étend le schéma, on crée une GPO, c’est tout.
- Il apporte ce que le legacy n’avait pas : mots de passe chiffrés, historique, sauvegarde DSRM des contrôleurs de domaine.
- L’ordre compte : retirer le MSI d’abord, supprimer les anciennes GPO en dernier.
- Le vrai piège, c’est la réplication : une GPO pas encore arrivée en SYSVOL sur le DC d’un site distant est ignorée.
Legacy, émulation, natif
| LAPS legacy | Windows LAPS (émulation) | Windows LAPS natif | |
|---|---|---|---|
| Installation | MSI + extension côté client (CSE) | Intégré | Intégré |
| Configuration | GPO AdmPwd.admx |
Lit la GPO legacy | GPO LAPS.admx |
| Stockage AD | ms-Mcs-AdmPwd, en clair |
ms-Mcs-AdmPwd, en clair |
msLAPS-EncryptedPassword, chiffré |
| Historique / DSRM | Non | Non | Oui |
Le mode émulation permet à un Windows récent d’appliquer une GPO legacy avec son moteur natif, à condition que le MSI ne soit pas installé. C’était déjà le cas sur mes stations récentes. Deux règles documentées dictent tout l’ordre de la migration :
- si le CSE legacy est installé, Windows LAPS lui laisse la main ;
- si une politique native existe, elle l’emporte sur la politique legacy.
L’état des lieux
Le contexte (anonymisé) : une forêt à deux domaines, srv.example.internal (serveurs) et ws.example.internal (stations), six DC en Server 2022 au niveau fonctionnel 2016, deux sites AD, une trentaine de machines Windows 11 et Server 2022, et un compte local géré appelé ici adm-local.
Quatre vérifications avant de toucher à quoi que ce soit : où sont liées les GPO legacy (Get-GPOReport), l’état du schéma, l’OS de chaque machine, et la présence du CSE legacy. Pour ce dernier point, on teste la clé de son extension :
Invoke-Command -ComputerName $machines {
Test-Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\GPExtensions\{D76B9641-3288-4f75-942D-087DE603E3EA}'
}
Le bilan :
- 100 % du parc était compatible avec le natif (Server 2019+ ou Windows 10/11 à jour depuis avril 2023), donc le legacy pouvait disparaître entièrement ;
- le schéma n’avait jamais été étendu ;
- deux contrôleurs de domaine avaient le CSE legacy installé, et deux autres portaient encore un mot de passe legacy en clair. C’étaient des restes de leur vie de serveur membre avant promotion. Sur un DC, ce CSE ferait tourner le mot de passe d’un compte de domaine si une politique legacy s’y appliquait. Je l’ai désinstallé et j’ai purgé les attributs d’abord.
L’ordre de bataille
Retirer le MSI en premier fait basculer chaque machine en émulation, avec la GPO legacy toujours en place. Il n’y a ainsi aucune interruption pendant qu’on prépare le natif.
Étape 1 : retirer le MSI
L’option Supprimer > Désinstaller immédiatement de la GPO de déploiement n’a rien désinstallé. Le package s’appelait « … (2) », signe d’un redéploiement passé, et l’installation logicielle par GPO ne retire que ce que ce déploiement précis a installé. J’ai donc désinstallé directement :
Invoke-Command srv-01, srv-02 {
(Start-Process msiexec -ArgumentList '/x {97E2CA7B-B657-4FF7-A6DB-30ECC73E1E28} /qn /norestart' -Wait -PassThru).ExitCode
}
Pour les machines injoignables, la même commande, conditionnée au Test-Path ci-dessus, tourne en script de démarrage dans la future GPO native.
Deux détails à retenir :
- les accolades :
msiexec /x {GUID}tapé tel quel dans PowerShell part sans le GUID, car{…}est lu comme un bloc de script, et avec/qnl’échec est silencieux ; - des Deny oubliés traînaient dans la délégation de la GPO, posés jadis pour contourner la boucle 24H2. Il faut les retirer.
Étape 2 : étendre le schéma
C’est l’opération irréversible du chantier. Avant de la lancer, je vérifie que repadmin /replsummary affiche 0 échec et je prends un snapshot du maître de schéma. Puis, avec un compte temporairement membre de Schema Admins :
Update-LapsADSchema -Verbose
La cmdlet ajoute six attributs ms-LAPS-*, un droit étendu, et les rattache à la classe computer. Juste après, je vide Schema Admins : chez moi, ce groupe reste vide en permanence.
Le repadmin /syncall suivant a renvoyé une erreur 8418 vers le site distant : les partitions de domaine avaient tenté de passer avant le nouveau schéma. Un second syncall quelques minutes plus tard était propre. C’est un transitoire classique.
Étape 3 : les permissions
Set-LapsADComputerSelfPermission -Identity 'OU=STATIONS,DC=ws,DC=example,DC=internal' -Domain ws.example.internal
$sid = (Get-ADGroup GRP-LAPS-Decryptors -Server srv.example.internal).SID.Value
Set-LapsADReadPasswordPermission -Identity 'OU=STATIONS,DC=ws,DC=example,DC=internal' -AllowedPrincipals $sid -Domain ws.example.internal
La première commande s’applique à chaque OU de machines, la seconde aux OU de machines hors DC. Le groupe de lecture est universel, car mon compte d’administration et les machines ne sont pas dans le même domaine.
Je passe le SID plutôt que le nom : le groupe venait d’être créé et le nom n’était pas encore résolvable depuis l’autre domaine (« Failed to resolve … to a sid »). Un SID ne dépend ni de la langue ni de la réplication.
Étape 4 : les GPO natives, en PowerShell
Quatre GPO à créer : LAPS_Native et LAPS_DC, dans chaque domaine. Les réglages LAPS ne sont que des valeurs de registre sous HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\LAPS, que Set-GPRegistryValue sait écrire :
$k = 'HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\LAPS'
function Set-LapsGpo($name, $domain, [switch]$DC) {
New-GPO -Name $name -Domain $domain | Out-Null
$v = @{ BackupDirectory=2; PasswordComplexity=4; PasswordLength=14; PasswordAgeDays=30
PasswordExpirationProtectionEnabled=1; ADPasswordEncryptionEnabled=1; ADEncryptedPasswordHistorySize=12 }
if ($DC) { $v.ADBackupDSRMPassword = 1 } else { $v.PostAuthenticationActions = 3; $v.PostAuthenticationResetDelay = 24 }
$v.GetEnumerator() | % { Set-GPRegistryValue -Name $name -Domain $domain -Key $k -ValueName $_.Key -Type DWord -Value $_.Value | Out-Null }
Set-GPRegistryValue -Name $name -Domain $domain -Key $k -ValueName ADPasswordEncryptionPrincipal -Type String -Value $sid | Out-Null
if (-not $DC) { Set-GPRegistryValue -Name $name -Domain $domain -Key $k -ValueName AdministratorAccountName -Type String -Value 'adm-local' | Out-Null }
}
Les choix à retenir :
- même compte qu’avant, ce qui rend la bascule transparente. Windows LAPS ne crée pas un compte au nom personnalisé : il doit déjà exister ;
- chiffrement, qui exige un niveau fonctionnel de domaine 2016 ;
- historique de 12, utile après la restauration d’une machine ;
- réinitialisation et fermeture de session 24 h après une utilisation du compte ;
LAPS_DCsauvegarde le DSRM. Pour ce compte, le déchiffreur configuré est ignoré : seuls les Admins du domaine du DC peuvent le lire.
Le piège : dans mon premier jet, j’avais écrit PwdExpirationProtectionEnabled, qui est le nom legacy. Le natif attend PasswordExpirationProtectionEnabled. Une GPO écrite en script ne prévient de rien. Seul le rapport HTML (Get-GPOReport -ReportType Html) trahissait l’erreur, en rangeant la valeur dans « Autres paramètres Registre ». Relisez toujours ce rapport.
Étape 5 : le pilote, et la question « quel DC ? »
J’ai pris un pilote par type de cible (station, serveur, DC), avec la GPO liée sur l’OU et un filtrage de sécurité limité à la machine pilote. Le serveur et le DC sont passés du premier coup. La station m’a donné trois leçons à la suite.
1. La GPO n’existait pas pour le poste. gpresult ne la listait nulle part. Le journal Microsoft-Windows-LAPS/Operational (événement 10055) montrait que le poste utilisait un DC du site distant. Sur ce DC, la GPO était en version 15 côté AD mais seulement 13 côté SYSVOL. Il a fallu forcer la réplication SYSVOL :
Invoke-Command dc-ws-03 {
Install-WindowsFeature RSAT-DFS-Mgmt-Con | Out-Null
dfsrdiag syncnow /partner:dc-ws-02 /RGName:"Domain System Volume" /Time:1
}
2. « Toujours legacy » d’après la console. Le poste avait bien écrit son mot de passe chiffré, mais sur le DC distant, alors que ma requête interrogeait un DC du site principal. Il faut interroger le bon DC. Attention, -Domain et -DomainController sont exclusifs :
Get-LapsADPassword pc-01 -DomainController dc-ws-03 -AsPlainText
3. DecryptionStatus : Unauthorized. Je m’étais ajouté au groupe de déchiffrement pendant la session, et le jeton Kerberos ne le savait pas encore. Une déconnexion suivie d’une reconnexion a suffi : Source : EncryptedPassword, DecryptionStatus : Success. La politique native avait aussi déclenché une rotation immédiate du mot de passe.
Côté DC, la source affichée est EncryptedDSRMPassword. Notez ce mot de passe : après une restauration système, c’est celui en vigueur au moment de la sauvegarde qui compte, et l’historique permet de le retrouver.
Étapes 6 et 7 : généraliser, puis faire le ménage
Dans l’ordre :
- j’ouvre le filtrage de
LAPS_Nativeà Utilisateurs authentifiés ; - ensuite seulement, je supprime
LAPS_deployetLAPS_config, pour qu’aucune machine ne se retrouve sans gestion ; - je purge les anciens mots de passe en clair, qu’une machine passée en natif ne nettoie pas :
Get-ADComputer -LDAPFilter '(&(ms-Mcs-AdmPwd=*)(msLAPS-EncryptedPassword=*))' -Server $d |
Set-ADComputer -Server $d -Clear 'ms-Mcs-AdmPwd','ms-Mcs-AdmPwdExpirationTime'
Une fois tout le parc passé, il reste à supprimer le partage du MSI, AdmPwd.admx/.adml et le script de démarrage. Les attributs legacy, eux, restent dans le schéma pour toujours.
Les erreurs classiques
- Supprimer les GPO legacy en premier : les machines restent sans gestion le temps que le natif arrive.
- Laisser le CSE legacy installé : Windows LAPS lui cède la main.
- Croire « Désinstaller immédiatement » sur parole : vérifiez la présence du CSE après redémarrage.
- Taper
msiexec /x {GUID}dans PowerShell : les accolades forment un bloc de script.- Ne pas relire le rapport d’une GPO scriptée : un nom de valeur faux est ignoré en silence.
- Conclure que « ça ne marche pas » sans vérifier quel DC le client utilise, ni si la GPO y est en AD et en SYSVOL.
- Oublier qu’un ajout de groupe exige une reconnexion.
- Oublier de purger
ms-Mcs-AdmPwd.
Ce que je retiens
La migration tient en une poignée de commandes. Le temps passe ailleurs : dans les restes d’une configuration vieille de plusieurs années, et dans une réplication multi-sites qui donne l’impression que rien ne marche alors que tout est en route. Trois réflexes ont fait la différence :
- inventorier avant d’agir ;
- lire le journal LAPS du client, en particulier les événements 10023 (source de la politique), 10055 (DC utilisé) et 10004 (succès) ;
- toujours se demander « quel DC ? ».
Le résultat : plus de mot de passe en clair dans l’annuaire, un historique, la sauvegarde DSRM des contrôleurs de domaine, et un MSI de moins sur un parc qui de toute façon le refusait.
Sources
- Microsoft Learn — What is Windows LAPS? : plateformes, dépréciation du legacy, mode émulation.
- Microsoft Learn — Migrate to Windows LAPS from legacy LAPS : scénarios, rotation immédiate, désinstallation du MSI.
- Microsoft Learn — Legacy Microsoft LAPS emulation mode.
- Microsoft Learn — Configure policy settings for Windows LAPS : noms des valeurs, prérequis du chiffrement, comportement DSRM.
- Microsoft Learn — Get-LapsADPassword.