PowerShell: valider des credentials

May 30, 2012

Couramment, avec PowerShell, on a besoin de travailler avec des login/password.

Microsoft propose la commande standard Get-Credential, mais cette dernière ne consistera qu’à afficher un popup d’authentification. Dans ce dernier, il est possible de saisir n’importe quoi, aucune validation ne sera faite. Avec du recul, on se rend compte que c’est logique. Un credential peut être saisi quel que soit le mécanisme d’authentification et la commande n’a pas pour vocation de les valider (ou ne peut techniquement pas).

Toutefois, dans monde très coloré Microsoft (pour un blog SharePoint c’est normal ;)), on va la plupart du temps travailler avec des comptes Active Directory. Dans ce cas, ne vaudrait-il pas mieux permettre de valider dans la foulée ces Credentials ?

Pour vous aider, voici un script PowerShell qui permet de valider un objet de type PSCredential :

[void][System.Reflection.Assembly]::LoadWithPartialName("System.DirectoryServices.AccountManagement")

function Validate-PSCredentials([System.Management.Automation.PSCredential]$credentials)
{
    $pctx = New-Object System.DirectoryServices.AccountManagement.PrincipalContext( `
                [System.DirectoryServices.AccountManagement.ContextType]::Domain)
    $nc = $credentials.GetNetworkCredential()
    return $pctx.ValidateCredentials($nc.UserName, $nc.Password)
}

L’élément clé de ce script est l’utilisation de la classe ContextType.Domain qui précise que le contexte est du type domaine. Il est aussi possible de spécifier le domaine si nécessaire.

En mettant en forme tout ceci, on peut créer une fonction PowerShell qui permet de valider la saisie de credentials et redemander le cas échéant à ressaisir le mot de passe :

function Get-ValidCredential
{
    param (
        [Parameter(Mandatory=$true)]
        [string]
        $username,
        [string]
        $candidatePassword
    )
    process {
        $passwordOk = $false
        $password = $candidatePassword
        $tries = 3
        do {
            if($password -ne $null -and $password -ne "") {
                $tries = 0
                $password = ConvertTo-SecureString "$password" -AsPlaintext -Force
            }
            else
            {
                $password = Read-Host -AsSecureString -Prompt "Please specify password for $username ($tries tries remaining)"
                $tries = $tries -1
            }

            $cred = New-Object System.Management.Automation.PSCredential $username, $password
            if(-not (Validate-PSCredentials $cred))
            {
                $password = $null
                Write-Warning "Wrong password."
                if($tries -eq 0) { throw "Cannot validate password for $username" }
            }
            else
            {
                return $cred
            }
        }while(-not $passwordOk)
    }
}

Ce script peut s’utiliser de deux manières : soit en spécifiant un mot de passe dans les arguments, soit en spécifiant un simple nom d’utilisateur et le mot de passe sera demandé de manière dynamique :

$cred = Get-ValidCredential "domain\user" "I know the password !"
$cred

$cred2 = Get-ValidCredential "domain\user"
$cred2