Si vous avez déjà essayé de modifier le mot de passe d’un compte local Windows via la ligne de commande (cmd ou PowerShell) sans être administrateur de la machine, vous vous êtes probablement heurté à un mur : si la boite de dialogue Ctrl+Alt+Suppr > Modifier un mot de passe permet bien de modifier le mot de passe de n’importe quel compte, les commandes disponibles nativement (net user ou Set-LocalUser) ne permettent elles que de réinitialiser celui d’un autre utilisateur que soi-même; ce qui nécessite évidemment des permissions élevées (Administrateur) sur la machine.
Les cas d’usage ne sont pas courants, mais on peut par exemple rencontrer le cas où un utilisateur simple d’un poste de travail dispose d’un second compte à privilèges sur sa machine qu’il utilise pour effectuer certaines tâches avec des permissions plus importantes via une élévation ponctuelle (RunAs). Les bonnes pratiques dictent que ce compte dispose d’un mot de passe à complexité élevée et à durée de vie faible, ce qui rend son renouvellement peu pratique s’il n’est pas possible d’utiliser de copier/coller, soit exactement la situation rencontrée dans la boite de dialogue Modifier un mot de passe.
Heureusement il est bel et bien possible de modifier ce mot de passe via Powershell à l’aide de la méthode native ChangePassword() de la classe System.DirectoryServices.AccountManagement.UserPrincipal, dont je vous présente ci-après une implémentation sous forme d’une fonction Set-LocalUserPassword facile à utiliser.
Exemple 1 : Changer le mot de passe pour l’utilisateur local « Bob », sans préciser son ancien ni son nouveau mot de passe dans la commande. La fonction se chargera de vous les demander interactivement.
Set-LocalUserPassword -Identity "Bob"
Exemple 2 : Changer votre propre mot de passe, sans préciser son ancien ni son nouveau mot de passe dans la commande. Dans ce cas pas la peine d’indiquer non-plus votre nom d’utilisateur, la fonction se chargera de tout vous demander interactivement.
Set-LocalUserPassword
Exemple 3 : L’ancien et le nouveau mot de passe peuvent aussi être fournis sous forme de SecureString dans la commande à l’aide des paramètres -OldPassword et -NewPassword.
$OldPassword = "P@ssword1" | ConvertTo-SecureString -AsPlainText -Force
$NewPassword = "P@ssword2" | ConvertTo-SecureString -AsPlainText -Force
Set-LocalUserPassword -Identity "Jane" -OldPassword $OldPassword -NewPassword $NewPassword
Le script complet est dispo sous form de gist Github : Changes the password of any local user account using its current password for verification without requiring administrator permissions
Ou directement ci-dessous :
Function Set-LocalUserPassword
{
<#
.SYNOPSIS
Changes the password of a local user account using the current password for verification.
.DESCRIPTION
This function utilizes the System.DirectoryServices.AccountManagement .NET namespace to change a local user's password.
Unlike the standard 'Set-LocalUser' cmdlet or 'net user' command, this function utilizes the 'ChangePassword' method.
This allows standard (non-administrator) users to change the password of any local account, provided they know the current password in the same way as with Set-ADAccountPassword for Active Directory Accounts.
.PARAMETER Identity
The SAMAccountName of the local user to modify.
If omitted, the function will prompt for the username.
If this prompt is left empty, it defaults to the currently logged-in user ($env:USERNAME).
.PARAMETER OldPassword
The current valid password for the account, passed as a SecureString.
If omitted, the function will prompt the user to enter it securely via the host UI.
.PARAMETER NewPassword
The desired new password for the account, passed as a SecureString.
If omitted, the function will prompt the user to enter it securely via the host UI.
.EXAMPLE
Set-LocalUserPassword -Identity "Bob"
Description
-----------
Attempts to change the password for local user "Bob". The function will interactively prompt for the Old and New passwords.
.EXAMPLE
Set-LocalUserPassword
Description
-----------
Prompts for the target username. If the user presses Enter without typing a name, it targets the current user.
Then prompts for the Old and New passwords.
.INPUTS
System.String
.OUTPUTS
None
.NOTES
Requirements: .NET Framework 3.5 or later (System.DirectoryServices.AccountManagement)
#>
[CmdletBinding()]
Param (
[Parameter(Mandatory = $false, Position = 0)]
[String]$Identity,
[Parameter(Mandatory = $false)]
[securestring]$OldPassword,
[Parameter(Mandatory = $false)]
[securestring]$NewPassword
)
Add-Type -AssemblyName System.DirectoryServices.AccountManagement
try
{
if ([string]::IsNullOrWhiteSpace($Identity))
{
$Identity = Read-Host -Prompt "Enter local username (Leave empty for current user)"
if ([string]::IsNullOrWhiteSpace($Identity))
{
$Identity = $env:USERNAME
}
}
Write-Verbose "Connecting to Local Machine Context..."
$PrincipalContext = [System.DirectoryServices.AccountManagement.PrincipalContext]::new("Machine")
Write-Verbose "Looking up user: $Identity"
$User = [System.DirectoryServices.AccountManagement.UserPrincipal]::FindByIdentity($PrincipalContext, $Identity)
if ($null -eq $User)
{
throw "User '$Identity' could not be found on this computer."
}
if (-not $OldPassword)
{
$OldPassword = Read-Host -Prompt "Enter OLD password for $Identity" -AsSecureString
}
if (-not $NewPassword)
{
$NewPassword = Read-Host -Prompt "Enter NEW password for $Identity" -AsSecureString
}
# Convert SecureString to PlainText as this is what ChangePassword() method requires
$OldPassPlain = [System.Net.NetworkCredential]::new("", $OldPassword).Password
$NewPassPlain = [System.Net.NetworkCredential]::new("", $NewPassword).Password
$User.ChangePassword($OldPassPlain, $NewPassPlain)
Write-Host "Password successfully changed for user: $Identity" -ForegroundColor Green
}
catch
{
Write-Error $_.Exception.Message
throw
}
finally
{
# Clean up unmanaged COM resources and plain text secret variables
if ($null -ne $User) { $User.Dispose() }
if ($null -ne $PrincipalContext) { $PrincipalContext.Dispose() }
$OldPassPlain = $null
$NewPassPlain = $null
}
}

0 commentaires