Functions/Common/Test-Credential.ps1
<#
.SYNOPSIS Test the provided credentials with the choosen test method against the local system or Active Directory. .DESCRIPTION Test the provided credentials against the local system by starting a simple process or against Active Directory by binding to the root via ADSI. .INPUTS System.Management.Automation.PSCredential .OUTPUTS System.Management.Automation.PSCredential System.Boolean .EXAMPLE PS C:\> Test-Credential -Credential 'DOMAIN\user' Test the interactive provided credentials against the local system. If the credential are not valid, an exception is thrown. .EXAMPLE PS C:\> Test-Credential -Credential 'DOMAIN\user' -Quiet Test the interactive provided credentials against the local system and return $true if the credentials are valid, else return $false. .EXAMPLE PS C:\> Test-Credential -Username $Username -Password $Password -Method ActiveDirectory Test the provided username and password pair against the Active Directory. .EXAMPLE PS C:\> $cred = Get-Credential 'DOMAIN\user' | Test-Credential Request the user to enter the password for DOMAIN\user and test it immediately with Test-Credential against the local system. If the credentials are valid, they are returned and stored in $cred. If not, an terminating exception is thrown. .NOTES Author : Claudio Spizzi License : MIT License .LINK https://github.com/claudiospizzi/SecurityFever #> function Test-Credential { [CmdletBinding()] [OutputType([System.Boolean])] [OutputType([System.Management.Automation.PSCredential])] param ( # PowerShell credentials object to test. [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ParameterSetName = 'Credential')] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential, # The username to validate. Specify password too. [Parameter(Mandatory = $true, ParameterSetName = 'UsernamePassword')] [System.String] $Username, # The password to validate. Specify username too. [Parameter(Mandatory = $true, ParameterSetName = 'UsernamePassword')] [System.Security.SecureString] $Password, # Validation method. [Parameter(Mandatory = $false)] [ValidateSet('StartProcess', 'ActiveDirectory')] [System.String] $Method = 'StartProcess', # Return a boolean value which indicates if the credentials are valid. [Parameter(Mandatory = $false)] [System.Management.Automation.SwitchParameter] $Quiet ) begin { if ($PSCmdlet.ParameterSetName -eq 'UsernamePassword') { $Credential = New-Object -TypeName PSCredential -ArgumentList $Username, $Password } } process { $exception = $null if ($Method -eq 'StartProcess') { Write-Verbose "Test credentials $($Credential.UserName) by starting a local cmd.exe process" try { # Create a new local process with the given credentials. This # does not validate the credentials against an external target # system, but tests if they are valid locally. Of courese, it's # possible to validate domain credentials too. $startInfo = New-Object -TypeName System.Diagnostics.ProcessStartInfo $startInfo.FileName = 'cmd.exe' $startInfo.WorkingDirectory = $env:SystemRoot $startInfo.Arguments = '/C', 'echo %USERDOMAIN%\%USERNAME%' $startInfo.Domain = $Credential.GetNetworkCredential().Domain $startInfo.UserName = $Credential.GetNetworkCredential().UserName $startInfo.Password = $Credential.GetNetworkCredential().SecurePassword $startInfo.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Hidden $startInfo.CreateNoWindow = $true $startInfo.UseShellExecute = $false $process = New-Object -TypeName System.Diagnostics.Process $process.StartInfo = $startInfo $process.Start() | Out-Null # If the process start does not throw an exception, the # credentials are valid. } catch { # Hide the $process.Start() method call exception, by expanding # the inner exception. if ($_.Exception.Message -like 'Exception calling "Start" with "0" argument(s):*') { $exception = $_.Exception.InnerException } else { $exception = $_.Exception } } } if ($Method -eq 'ActiveDirectory') { Write-Verbose "Test credentials $($Credential.UserName) by binding the default domain with ADSI" try { # We use an empty path, because we just test the credential # binding and not any object access in Active Directory. $directoryEntryArgs = @{ TypeName = 'System.DirectoryServices.DirectoryEntry' ArgumentList = '', # Bind to the local default domain $Credential.GetNetworkCredential().UserName, $Credential.GetNetworkCredential().Password } $directoryEntry = New-Object @directoryEntryArgs -ErrorAction Stop if ($null -eq $directoryEntry -or [String]::IsNullOrEmpty($directoryEntry.distinguishedName)) { throw 'Unable to create an ADSI connection.' } } catch { $exception = $_.Exception } } # Check the exception variable if an exception occured and return a # boolean or the credentials. In case of an exception, throw a custom # exception. if ($Quiet.IsPresent) { Write-Output ($null -eq $exception) } else { if ($null -eq $exception) { Write-Output $Credential } else { $errorRecordArgs = $exception, '0', [System.Management.Automation.ErrorCategory]::AuthenticationError, $Credential $errorRecord = New-Object -TypeName 'System.Management.Automation.ErrorRecord' -ArgumentList $errorRecordArgs $PSCmdlet.ThrowTerminatingError($errorRecord) } } } } |