Skip to content

Instantly share code, notes, and snippets.

@PanosGreg
Last active March 5, 2026 13:30
Show Gist options
  • Select an option

  • Save PanosGreg/8a027b079eac32ba72afdea50343a136 to your computer and use it in GitHub Desktop.

Select an option

Save PanosGreg/8a027b079eac32ba72afdea50343a136 to your computer and use it in GitHub Desktop.
Verify Windows credentials (either in Active Directory or Locally)
function Test-WindowsCredential {
<#
.Synopsis
Verify Active Directory and/or Local credentials
.EXAMPLE
Test-WindowsCredential -Username user1 -Password Secret01
.EXAMPLE
$creds = Get-WindowsCredential
Test-WindowsCredential -Credential $creds
.EXAMPLE
Test-WindowsCredential -Username user1 -Password Secret01 -PassThru
#>
[CmdletBinding(DefaultParameterSetName = 'PSCreds')]
[OutputType([Boolean],[object])] # <-- by default returns a bool, and with PassThru it returns an object
Param (
[Parameter(Mandatory=$true,Position=0,ParameterSetName = 'PlainText')]
[string]$Username,
[Parameter(Mandatory=$true,Position=1,ParameterSetName = 'PlainText')]
[string]$Password,
[Parameter(Mandatory=$true,Position=0,ParameterSetName = 'PSCreds')]
[pscredential]$Credential,
[Validateset('Domain','Machine','ApplicationDirectory')] # AppDirectory is for ADAM
[string]$Context = 'Domain',
[string]$Server, # <-- optional parameter, can take either a hostname or an IP address
[switch]$PassThru, # <-- return an object with more info instead of a boolean
[switch]$DontCheckProfile
)
# get the user/pass from the pscreds
if ($PSCmdlet.ParameterSetName -eq 'PSCreds') {
$Username = $Credential.UserName
$Password = $Credential.GetNetworkCredential().Password
}
$WillContinue = $true # <-- we'll use this flag to skip parts if need be
# strip the domain from the username (the username argument must not be in the form of domain\username or username@domain)
if ($Username.IndexOf('\') -ge 1) {$Username = $Username.Split('\')[1]}
elseif ($Username.IndexOf('@') -ge 1) {$Username = $Username.Split('@')[0]}
# see if the user's profile already exists in the system
if (-not $DontCheckProfile) {
# get the domain we are currently in
if ($env:USERDOMAIN -eq 'WORKGROUP') {$CurrentDomain = $env:COMPUTERNAME} # <-- this happens if running as SYSTEM
else {$CurrentDomain = $env:USERDOMAIN}
$NtAccount = [Security.Principal.NTAccount]::new($CurrentDomain, $Username)
# find the Security ID (SID) of the user
try {$UserSID = $NtAccount.Translate([Security.Principal.SecurityIdentifier]).Value}
catch {
if (-not $PassThru) {Write-Warning "Could not find user $Username in $CurrentDomain"}
$CanLogin = $false
$LoginError = $_.Exception
$WillContinue = $false
}
if ($WillContinue) {
# load the CimCmdlets module
if ((Get-Module).Name -notcontains 'CimCmdlets') {Import-Module -Name CimCmdlets -Verbose:$false}
# get the local user profile
$UserProfile = Get-CimInstance -ClassName Win32_UserProfile -Filter "SID = '$UserSID'" -Verbose:$false
# inform the user that the profile will be created
if ($null -eq $UserProfile) {
Write-Verbose "The user $Username does not have a profile on this computer ($env:COMPUTERNAME)"
Write-Verbose 'The credential check will take a bit longer, because the user profile will also be created for the 1st time.'
}
} #if WillContinue
} #if check profile
# load the .net type for DirectoryServices
if (-not ('System.DirectoryServices.AccountManagement.ContextType' -as [type]) -and $WillContinue) {
Add-Type -AssemblyName System.DirectoryServices.AccountManagement -ErrorAction Stop
}
# check the account
if ($WillContinue) {
$LoginDuration = Measure-Command -Expression {
$CtxType = [System.DirectoryServices.AccountManagement.ContextType]::$Context
try {
if ($PSBoundParameters.ContainsKey('Server')) {
$Principal = [System.DirectoryServices.AccountManagement.PrincipalContext]::new($CtxType,$Server) # <-- this is the connection to the server
}
else {
$Principal = [System.DirectoryServices.AccountManagement.PrincipalContext]::new($CtxType)
}
$CanLogin = $Principal.ValidateCredentials($Username, $Password) # <-- this is the actual validation
}
catch {
if (-not $PassThru) {Write-Warning "There was an error when trying the credential in the '$Context' context"}
$CanLogin = $false
$LoginError = $_.Exception # <-- alternatively we could do: "Resolve-Error $_" if we have the Resolve-Error function
$WillContinue = $false
}
} #Measure-Command
} #if WillContinue
# throw if erroraction is stop
if ($ErrorActionPreference -eq 'Stop' -and $LoginError) {
$PSCmdlet.ThrowTerminatingError((Write-Error -Exception $LoginError 2>&1))
}
# show the output
if ($PassThru) {
$out = [pscustomobject]@{
PSTypeName = 'Credential.Test'
Context = $Context
LoginFrom = $env:COMPUTERNAME
LoginTo = $Principal.ConnectedServer
LoginAsUser = $Username
CanLogin = $CanLogin
LoginDuration = $LoginDuration
}
if ($LoginError) {$out | Add-Member -NotePropertyMembers @{LoginError = $LoginError}}
}
else {$out = $CanLogin} # <-- boolean
# clean up
if ($Principal) {$Principal.Dispose()}
# show the output
Write-Output $out
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment