Jump to page sections
To test PowerShell remoting, introduced in PowerShell version 2, you can use the method described in Lee Holmes' article here. For large-scale testing of remoting against possibly thousands of computers in an enterprise, I use the same method, wrapped in a script that uses runspaces for concurrency. The runspace model was borrowed from Zachary Loeber (thanks to both).

Should work with PowerShell versions 2 and up (tested with: 2, 3, 4).

The end result should be a pretty awesome enterprise PowerShell remoting test tool. I have used it against close to 4000 servers in one go, and it performed admirably. It should process a few hundred servers (100-500?) per minute normally. Beware of periodically potentially high CPU usage on the server that runs the script - multiple cores recommended.

I thought about enforcing order of properties, but it's just three simple properties, so I might put it off for a good while. The properties returned are ComputerName (string), Success ($true if it worked, $null on error, should have made it a bool), and Error (string). Happy Select-Objecting for output order.

Here it is running in my lab environment:


Download

*Test-PSRemoting.ps1.txt - right-click and save/download.

Earlier versions (if any):
Test-PSRemoting.ps1.txt

Usage

Process the results. You need PowerShell v3 or up to use -WarningVariable.


Look at everything in Out-GridView (ogv):


Parameters

{| border="3" cellpadding="8" |- |ComputerName |Target computers. |- |PromptForCredentials |Prompt for alternate credentials (also see -Credential) |- |Credential |Supply a PSCredential object with alternate credentials. |- |ThrottleLimit |Number of concurrent runspaces. |- |HideProgress |Do not display progress bar using Write-Progress. |- |Timeout |Individual runspace timeout in seconds. Default: 30. |- |Quiet |Do not display end summary with start and end time using Write-Host. |}

Code

<#
.SYNOPSIS
Check remote target computers for PowerShell remoting capabilities.
Uses runspaces for concurrency.

Copyright (C) 2015, Svendsen Tech
All rights reserved.
Author: Joakim Borger Svendsen

.EXAMPLE
.\Test-PSRemoting.ps1 -ComputerName $Servers -Credential $mycred
.EXAMPLE
.\Test-PSRemoting.ps1 -ComputerName $Servers | Format-Table -AutoSize
.EXAMPLE
$Results = .\Test-PSRemoting.ps1 -ComputerName $Servers
#>

[CmdletBinding()]
param(
    # Target computer names.
    [Parameter(ValueFromPipeline=$true,
               ValueFromPipelineByPropertyName=$true,
               Mandatory=$true)][Alias('Cn', 'Hostname', 'PSComputerName')]
        [string[]] $ComputerName,
        # Prompt for credentials to be used when connecting.
        [switch] $PromptForCredentials,
        # Supply PSCredentials object to be used when connecting.
        [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty,
        # Number of simultaneously running threads.
        [int] $ThrottleLimit = 32,
        # Do not display progress using Write-Progress.
        [switch] $HideProgress,
        # Timeout in seconds.
        [int] $Timeout = 30,
        # Don't display end summary showing start and end time using Write-Host.
        [switch] $Quiet
)

begin {
    $MyEAP = 'Stop'
    $ErrorActionPreference = $MyEAP
    $StartTime = Get-Date
    if ($PromptForCredentials) {
        $Credential = Get-Credential
    }
    $RunspaceTimers = [HashTable]::Synchronized(@{})
    $Runspaces = New-Object -TypeName System.Collections.ArrayList
    $RunspaceCounter = 0
    Write-Verbose -Message 'Creating initial session state.'
    $ISS = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
    $ISS.Variables.Add((New-Object -TypeName System.Management.Automation.Runspaces.SessionStateVariableEntry -ArgumentList 'RunspaceTimers', (Get-Variable -Name 'RunspaceTimers' -ValueOnly), ''))
    Write-Verbose -Message 'Creating runspace pool.'
    $RunspacePool = [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspacePool(1, $ThrottleLimit, $ISS, $Host)
    $RunspacePool.ApartmentState = 'STA'
    $RunspacePool.Open()
    # This is the script block that is run for each computer.
    $ScriptBlock = {
        [CmdletBinding()]
        param(
            [string] $Computer,
            [int] $ID,
            $Credential
        )
        # Get the start time.
        $RunspaceTimers.$ID = Get-Date
        # The objects returned here are passed to the host...
        if (-not (Test-Connection $Computer -Count 1 -Quiet)) {
            New-Object psobject -Property @{
                ComputerName = $Computer
                Success = $null
                Error   = 'No ping reply'
            }
            continue
        }
        $IcmHash = @{
            ErrorAction = 'Stop'
            ComputerName = $Computer
            ScriptBlock = { 'It works' }
        }
        if ($Credential.Username -ne $null) {
            $IcmHash.Credential = $Credential
        }
        try {
            $Result = Invoke-Command @IcmHash
        }
        catch {
            New-Object psobject -Property @{
                ComputerName = $Computer
                Success = $null
                Error = $_
            }
            continue
        }
        # Check if results are as expected.
        if ($Result -ne 'It works') {
            New-Object psobject -Property @{
                ComputerName = $Computer
                Success = $null
                Error = 'Unknown error'
            }
            continue
        }
        # Everything went well, return success object.
        New-Object psobject -Property @{
            ComputerName = $Computer
            Success = $true
            Error = $null
        }
    } # end of script block
    
    function Get-PSRemotingResult {
        [CmdletBinding()]
        param( [switch]$Wait )
        do
        {
            $More = $false
            foreach ($Runspace in $Runspaces) {
                $StartTime = $RunspaceTimers[$Runspace.ID]
                if ($Runspace.Handle.IsCompleted)
                {
                    Write-Verbose -Message ('Thread done for {0}' -f $Runspace.IObject)
                    $Runspace.PowerShell.EndInvoke($Runspace.Handle)
                    $Runspace.PowerShell.Dispose()
                    $Runspace.PowerShell = $null
                    $Runspace.Handle = $null
                }
                elseif ($Runspace.Handle -ne $null) {
                    $More = $true
                }
                if ($Timeout -and $StartTime) {
                    if ((New-TimeSpan -Start $StartTime).TotalSeconds -ge $Timeout -and $Runspace.PowerShell) {
                        Write-Warning -Message ('Timeout {0}' -f $Runspace.IObject)
                        $Runspace.PowerShell.Dispose()
                        $Runspace.PowerShell = $null
                        $Runspace.Handle = $null
                    }
                }
            }
            if ($More -and $PSBoundParameters['Wait']) {
                Start-Sleep -Milliseconds 100
            }
            foreach ($Thread in $Runspaces.Clone()) {
                if (-not $Thread.Handle) {
                    Write-Verbose -Message ('Removing {0} from runspaces' -f $Thread.IObject)
                    $Runspaces.Remove($Thread)
                }
            }
            if (-not $HideProgress) {
                $ProgressSplatting = @{
                    Activity = 'Testing PSRemoting capabilities'
                    Status = 'Processing: {0} of {1} total threads done' -f ($RunspaceCounter - $Runspaces.Count), $RunspaceCounter
                    PercentComplete = ($RunspaceCounter - $Runspaces.Count) / $RunspaceCounter * 100
                }
                Write-Progress @ProgressSplatting
            }
        }
        while ($More -and $PSBoundParameters['Wait'])
    } # end of Get-PSRemotingResult
} # end of begin block

process {
    foreach ($Computer in $ComputerName) {
        $RunspaceCounter++
        $psCMD = [System.Management.Automation.PowerShell]::Create().AddScript($ScriptBlock)
        [void] $psCMD.AddParameter('ID', $RunspaceCounter)
        [void] $psCMD.AddParameter('Computer', $Computer)
        [void] $psCMD.AddParameter('Credential', $Credential)
        [void] $psCMD.AddParameter('Verbose', $VerbosePreference)
        $psCMD.RunspacePool = $RunspacePool
        Write-Verbose -Message "Testing PSRemoting capabilities: Checking $Computer"
        [void]$Runspaces.Add(@{
            Handle = $psCMD.BeginInvoke()
            PowerShell = $psCMD
            IObject = $Computer
            ID = $RunspaceCounter
        })
        Get-PSRemotingResult
    }
}

end {
    Get-PSRemotingResult -Wait
    if (-not $HideProgress) {
        Write-Progress -Activity 'Testing PSRemoting capabilities' -Status 'Done' -Completed
    }
    Write-Verbose -Message 'Closing runspace pool.'
    $RunspacePool.Close()
    $RunspacePool.Dispose()
    if (-not $Quiet) {
        Write-Host -ForegroundColor Green ('Start time: ' + $StartTime)
        Write-Host -ForegroundColor Green ('End time:   ' + (Get-Date))
    }
}
Powershell      Windows          All Categories

Google custom search of this website only

Minimum cookies is the standard setting. This website uses Google Analytics and Google Ads, and these products may set cookies. By continuing to use this website, you accept this.

If you want to reward my efforts