Skip to content

Instantly share code, notes, and snippets.

@SweetAsNZ
Last active November 25, 2025 01:33
Show Gist options
  • Select an option

  • Save SweetAsNZ/d8b85e4e1194211066282edb3c95d89c to your computer and use it in GitHub Desktop.

Select an option

Save SweetAsNZ/d8b85e4e1194211066282edb3c95d89c to your computer and use it in GitHub Desktop.
Test WinRM Health
function Test-WinRMHealth {
<#
.EXAMPLE
# Local auto-detect and try-all
Test-WinRMHealth
.EXAMPLE
# Remote over HTTP (default 5985), auto-try enabled auths
Test-WinRMHealth -ComputerName 'srv01'
.EXAMPLE
# Remote over HTTPS (default 5986), auto-try
Test-WinRMHealth -ComputerName 'srv01.domain.local' -UseSSL
.EXAMPLE
# Force a specific auth only
Test-WinRMHealth -ComputerName 'srv01' -Authentication Kerberos
.EXAMPLE
# Use explicit creds (needed for Basic or when your current token can’t auth)
$cred = Get-Credential
Test-WinRMHealth -ComputerName 'edge-host' -UseSSL -Authentication Basic -Credential $cred
#>
[CmdletBinding()]
param(
[Parameter(ValueFromPipelineByPropertyName, ValueFromPipeline)]
[string]$ComputerName = 'localhost',
[switch]$UseSSL,
[int]$Port,
[ValidateSet('Default','Kerberos','Negotiate','Basic','CredSSP')]
[string]$Authentication = 'Default',
[System.Management.Automation.PSCredential]$Credential
)
begin {
$results = @()
function _IsLocal {
param([string]$Name)
try {
$trim = $Name.Trim('.')
if ($trim -eq '' -or $trim -ieq 'localhost' -or $trim -ieq $env:COMPUTERNAME) { return $true }
$fqdn = try { ([System.Net.Dns]::GetHostEntry($env:COMPUTERNAME)).HostName } catch { $null }
if ($fqdn -and $trim -ieq $fqdn) { return $true }
return $false
} catch { return $false }
}
function _safe {
param([ScriptBlock]$ScriptBlock)
try { & $ScriptBlock } catch { $_ }
}
}
process {
$target = $ComputerName
$localTarget = _IsLocal -Name $target
if (-not $Port) { $Port = ($(if ($UseSSL) { 5986 } else { 5985 })) }
# ---------------- Client config (local) ----------------
$clientAuth = _safe { Get-Item -Path WSMan:\localhost\Client\Auth | Select-Object * }
$clientCfg = _safe { Get-Item -Path WSMan:\localhost\Client | Select-Object * }
$trustedHosts = _safe { (Get-Item WSMan:\localhost\Client\TrustedHosts -ErrorAction Stop).Value }
$allowUnenc = $null
if ($clientCfg -and $clientCfg.PSObject.Properties['AllowUnencrypted']) { $allowUnenc = [bool]$clientCfg.AllowUnencrypted }
# Build the candidate auth list from current client config (order matters)
$enabledAuths = @()
if ($clientAuth -and $clientAuth.PSObject.Properties['Kerberos'] -and $clientAuth.Kerberos) { $enabledAuths += 'Kerberos' }
if ($clientAuth -and $clientAuth.PSObject.Properties['Negotiate'] -and $clientAuth.Negotiate) { $enabledAuths += 'Negotiate' }
if ($clientAuth -and $clientAuth.PSObject.Properties['CredSSP'] -and $clientAuth.CredSSP) { $enabledAuths += 'CredSSP' }
if ($clientAuth -and $clientAuth.PSObject.Properties['Basic'] -and $clientAuth.Basic) { $enabledAuths += 'Basic' }
# If user forced a specific method, use just that; else try Default then each enabled auth
$authQueue = @()
if ($Authentication -ne 'Default') {
$authQueue = @($Authentication)
} else {
$authQueue = @('Default') + $enabledAuths
}
# --------------- Server-side snapshot (local only) ---------------
$serverSummary = $null
if ($localTarget) {
$svc = _safe { Get-Service -Name WinRM -ErrorAction Stop }
$listeners = _safe {
$items = Get-ChildItem WSMan:\localhost\Listener -ErrorAction Stop
foreach ($i in $items) {
[PSCustomObject]@{
Transport = $i.Keys['Transport']
Port = $i.Keys['Port']
Address = $i.Keys['Address']
Hostname = $i.Keys['Hostname']
CertificateThumbprint = $i.Keys['CertificateThumbprint']
Enabled = $i.ValueSet['Enabled']
}
}
}
$fw = _safe {
Get-NetFirewallRule -ErrorAction Stop |
Where-Object { $_.DisplayName -match 'Windows Remote Management' } |
Select-Object DisplayName, Enabled, Direction, Profile
}
$svcStatus = if ($svc -is [System.ServiceProcess.ServiceController]) { $svc.Status } else { $svc }
$serverSummary = [PSCustomObject]@{
Type = 'Server'
Machine = $env:COMPUTERNAME
WinRMService = $svcStatus
Listeners = $listeners
Firewall = $fw
}
} else {
$serverSummary = [PSCustomObject]@{
Type = 'Server'
Machine = $target
WinRMService = 'Unknown (remote query requires WSMan)'
Listeners = $null
Firewall = $null
}
}
# --------------- Connectivity baseline ----------------
$endpoint = "http{0}://{1}:{2}/wsman" -f ($(if($UseSSL){'s'}else{''}), $target, $Port)
$tnc = _safe { Test-NetConnection -ComputerName $target -Port $Port -WarningAction SilentlyContinue }
$tncOK = $false
if ($tnc -and $tnc.PSObject.Properties['TcpTestSucceeded']) { $tncOK = [bool]$tnc.TcpTestSucceeded }
# --------------- Try all viable auths ----------------
$attempts = @()
foreach ($auth in $authQueue) {
# Skip obviously bad combos: Basic over HTTP with client disallowing unencrypted
if ($auth -eq 'Basic' -and -not $UseSSL -and $allowUnenc -ne $true) {
$attempts += [PSCustomObject]@{
Authentication = $auth
UseSSL = [bool]$UseSSL
Port = $Port
Result = 'Skipped'
Reason = 'Client AllowUnencrypted = false; Basic over HTTP would send creds in cleartext'
}
continue
}
$wsmanParams = @{
ComputerName = $target
ErrorAction = 'Stop'
Port = $Port
}
if ($UseSSL) { $wsmanParams['UseSSL'] = $true }
if ($auth -ne 'Default') { $wsmanParams['Authentication'] = $auth }
if ($Credential) { $wsmanParams['Credential'] = $Credential }
$ok = $false
$err = $null
$detail = $null
try {
$res = Test-WSMan @wsmanParams
if ($res) {
$ok = $true
$detail = 'Identify succeeded'
} else {
$detail = 'No response object returned'
}
} catch {
$err = $_.Exception.Message
}
$attempts += [PSCustomObject]@{
Authentication = $auth
UseSSL = [bool]$UseSSL
Port = $Port
Result = if ($ok) { 'Success' } elseif ($err) { 'Failed' } else { 'Unknown' }
Detail = if ($ok) { $detail } else { $err }
}
# If Default worked, we’re done. No need to smash the server with more requests.
if ($ok -and $auth -eq 'Default') { break }
}
# --------------- Pick the verdict ----------------
$firstSuccess = $attempts | Where-Object { $_.Result -eq 'Success' } | Select-Object -First 1
$status = if ($firstSuccess) { 'Healthy' } else { 'Unhealthy' }
$reasonText = $null
if ($firstSuccess) {
$reasonText = "WSMan OK via $($firstSuccess.Authentication)"
} else {
if ($tncOK) {
$reasonText = 'Port open, WSMan handshake failed for all tried auth methods'
} else {
$reasonText = 'Port closed or filtered'
}
}
# --------------- Pack summaries ----------------
$clientSummary = [PSCustomObject]@{
Type = 'Client'
Machine = $env:COMPUTERNAME
WinRMService = (_safe { (Get-Service -Name WinRM -ErrorAction Stop).Status })
Auth_Basic = if ($clientAuth -and $clientAuth.PSObject.Properties['Basic']) { $clientAuth.Basic } else { $null }
Auth_Kerberos = if ($clientAuth -and $clientAuth.PSObject.Properties['Kerberos']) { $clientAuth.Kerberos } else { $null }
Auth_Negotiate = if ($clientAuth -and $clientAuth.PSObject.Properties['Negotiate']) { $clientAuth.Negotiate } else { $null }
Auth_CredSSP = if ($clientAuth -and $clientAuth.PSObject.Properties['CredSSP']) { $clientAuth.CredSSP } else { $null }
AllowUnencrypted = $allowUnenc
TrustedHosts = $trustedHosts
}
$connectivity = [PSCustomObject]@{
Type = 'Connectivity'
Target = $target
Endpoint = $endpoint
TcpReachable = $tncOK
TcpDetail = if ($tncOK) { 'TCP succeeded' } else { ($tnc | Out-String).Trim() }
Attempts = $attempts
}
$health = [PSCustomObject]@{
Target = $target
Status = $status
Reason = $reasonText
Client = $clientSummary
Server = $serverSummary
Connectivity = $connectivity
Timestamp = (Get-Date)
}
$results += $health
}
end {
$results
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment