Public/network/Test-WinRM.ps1
|
#Requires -Version 5.1 function Test-WinRM { <# .SYNOPSIS Tests WinRM connectivity and configuration on remote computers .DESCRIPTION Performs a comprehensive WinRM connectivity test on both HTTP (5985) and HTTPS (5986) by default: 1. Tests TCP port reachability 2. Tests WSMan connection via Test-WSMan 3. Tests actual command execution via Invoke-Command Returns two rows per computer (HTTP + HTTPS), giving a complete picture in a single call. Use -Protocol to test only one protocol. .PARAMETER ComputerName One or more computer names to test. Accepts pipeline input. .PARAMETER Credential Optional credential for authentication. .PARAMETER Protocol Protocol(s) to test. Default: both HTTP and HTTPS. Valid values: HTTP, HTTPS. .PARAMETER TimeoutMs TCP port test timeout in milliseconds. Default: 3000. .EXAMPLE Test-WinRM -ComputerName 'SRV01' Tests WinRM on SRV01 over both HTTP (5985) and HTTPS (5986). .EXAMPLE Test-WinRM -ComputerName 'SRV01' -Protocol HTTP Tests WinRM on SRV01 over HTTP only. .EXAMPLE Test-WinRM -ComputerName 'SRV01' -Credential (Get-Credential) Full test with credentials (both protocols). .EXAMPLE 'SRV01', 'SRV02', 'SRV03' | Test-WinRM Pipeline: tests WinRM on 3 servers (both protocols each). .OUTPUTS PSWinOps.WinRMTestResult .NOTES Author: Franck SALLET Version: 1.1.0 Last Modified: 2026-03-22 Requires: PowerShell 5.1+ / Windows only Permissions: No admin required for testing, target must allow WinRM .LINK https://github.com/k9fr4n/PSWinOps #> [CmdletBinding()] [OutputType('PSWinOps.WinRMTestResult')] param ( [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [ValidateNotNullOrEmpty()] [Alias('CN', 'Name', 'DNSHostName')] [string[]]$ComputerName, [Parameter(Mandatory = $false)] [PSCredential]$Credential, [Parameter(Mandatory = $false)] [ValidateSet('HTTP', 'HTTPS')] [string[]]$Protocol = @('HTTP', 'HTTPS'), [Parameter(Mandatory = $false)] [ValidateRange(500, 30000)] [int]$TimeoutMs = 3000 ) begin { Write-Verbose "[$($MyInvocation.MyCommand)] Starting WinRM tests (Protocol: $($Protocol -join ', '))" $hasCredential = $PSBoundParameters.ContainsKey('Credential') $protocolMap = @{ 'HTTP' = @{ Port = 5985; UseSSL = $false } 'HTTPS' = @{ Port = 5986; UseSSL = $true } } } process { foreach ($targetComputer in $ComputerName) { foreach ($proto in $Protocol) { try { $winrmPort = $protocolMap[$proto].Port $useSSL = $protocolMap[$proto].UseSSL $portOpen = $false $wsmanOK = $false $execOK = $null $wsmanVersion = $null $errorMessage = $null Write-Verbose "[$($MyInvocation.MyCommand)] Testing '$targetComputer' $proto (port $winrmPort)" # Step 1: TCP port test $tcpClient = $null try { $tcpClient = New-Object System.Net.Sockets.TcpClient $connectTask = $tcpClient.ConnectAsync($targetComputer, $winrmPort) $portOpen = $connectTask.Wait($TimeoutMs) -and -not $connectTask.IsFaulted } catch { Write-Verbose "[$($MyInvocation.MyCommand)] Port $winrmPort closed on '$targetComputer': $_" } finally { if ($tcpClient) { $tcpClient.Close(); $tcpClient.Dispose() } } # Step 2: WSMan test (only if port is open) if ($portOpen) { try { $wsmanParams = @{ ComputerName = $targetComputer ErrorAction = 'Stop' } if ($useSSL) { $wsmanParams['UseSsl'] = $true } if ($hasCredential) { $wsmanParams['Credential'] = $Credential } $wsmanResult = Test-WSMan @wsmanParams $wsmanOK = $true $wsmanVersion = $wsmanResult.ProductVersion } catch { $errorMessage = "WSMan failed: $($_.Exception.Message)" Write-Verbose "[$($MyInvocation.MyCommand)] WSMan test failed on '$targetComputer' ($proto): $_" } } else { $errorMessage = "Port $winrmPort is not reachable" } # Step 3: Execution test (always when WSMan succeeds) if ($wsmanOK) { try { $invokeParams = @{ ComputerName = $targetComputer ScriptBlock = { $env:COMPUTERNAME } ErrorAction = 'Stop' } if ($useSSL) { $invokeParams['UseSSL'] = $true } if ($hasCredential) { $invokeParams['Credential'] = $Credential } $execResult = Invoke-Command @invokeParams $execOK = ($null -ne $execResult) } catch { $execOK = $false $errorMessage = "Execution failed: $($_.Exception.Message)" Write-Verbose "[$($MyInvocation.MyCommand)] Execution test failed on '$targetComputer' ($proto): $_" } } [PSCustomObject]@{ PSTypeName = 'PSWinOps.WinRMTestResult' ComputerName = $targetComputer Port = $winrmPort Protocol = $proto PortOpen = $portOpen WSManConnected = $wsmanOK ExecutionOK = $execOK WSManVersion = $wsmanVersion ErrorMessage = $errorMessage Timestamp = Get-Date -Format 'o' } } catch { Write-Error "[$($MyInvocation.MyCommand)] Failed on '$targetComputer' ($proto): $_" } } } } end { Write-Verbose "[$($MyInvocation.MyCommand)] Completed WinRM tests" } } |