Functions/Test-Credential.ps1

<#
    .SYNOPSIS
    Test the provided credentials with the choosen test method.

    .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
    None.

    .OUTPUTS
    System.Boolean. Indicates if the credentials are valid or not.

    .EXAMPLE
    PS C:\> Test-Credential -Credential 'DOMAIN\user'
    Test the interactive provided credentials against the local system.

    .EXAMPLE
    PS C:\> Test-Credential -Credential 'DOMAIN\user' -Throw
    Test the interactive provided credentials against the local system, and
    throws an exception if the credentials are not valid.

    .EXAMPLE
    PS C:\> Test-Credential -Username $Username -Password $Password -Method ActiveDirectory
    Test the provided username and password pair against the Active Directory.

    .NOTES
    Author : Claudio Spizzi
    License : MIT License

    .LINK
    https://github.com/claudiospizzi/SecurityFever
#>


function Test-Credential
{
    [CmdletBinding()]
    [OutputType([System.Boolean])]
    param
    (
        # PowerShell credentials object to test.
        [Parameter(Mandatory = $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',

        # Throw an terminating exception if the credentials are not valid.
        [Parameter(Mandatory = $false)]
        [System.Management.Automation.SwitchParameter]
        $Throw
    )

    begin
    {
        if ($PSCmdlet.ParameterSetName -eq 'UsernamePassword')
        {
            $Credential = New-Object -TypeName PSCredential -ArgumentList $Username, $Password
        }
    }

    process
    {
        try
        {
            if ($Method -eq 'StartProcess')
            {
                # 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.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 ($Method -eq 'ActiveDirectory')
            {
                # 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 = '',
                                    $Credential.GetNetworkCredential().UserName,
                                    $Credential.GetNetworkCredential().Password
                }
                $directoryEntry = New-Object @directoryEntryArgs

                if ($null -eq $directoryEntry)
                {
                    throw 'Unable to create an ADSI connection.'
                }
            }

            if (-not $Throw.IsPresent)
            {
                return $true
            }
        }
        catch
        {
            Write-Warning -Message "Credential validation failed: $_"

            if ($Throw.IsPresent)
            {
                throw "Credential validation failed: $_"
            }
            else
            {
                return $false
            }
        }
    }
}