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. Defaults to the local computer. Accepts pipeline input by value and by property name. .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 Tests WinRM on the local computer over both HTTP (5985) and HTTPS (5986). .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.2.0 Last Modified: 2026-04-02 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 = $false, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [ValidateNotNullOrEmpty()] [Alias('CN', 'Name', 'DNSHostName')] [string[]]$ComputerName = $env: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" } } |