Tuesday, December 18, 2012

POSH What do I need to do to get a ping? (test-connection out of resources)

I frequently write powershell scripts that run against a large number of machines. It has been put out in many places that for best practices, and error handling you should check connectivity to the remote machine first. This make sense. Another best practice typically thrown around is to use existing cmdlets. This of course saves the time of writing your own code, however I noticed with test-connection, it seems to have problems when you're running fast code on a large number of machines. If we use the existing Test-Connection in an IF statement for true/false checks of the machine being accessible or not, you may end up at a point where your machine is unable to process the command due to resource issues.

Error: Test-Connection : Testing connection to computer '10.0.0.1' failed: Error due to lack of resources

This is a problem as one of your attempts to handle errors becomes an error by itself. You can get around this with a few other methods. There is the standard ping.exe command which will return different error codes for status. You could use that and read the return to see if it evaluates successfully. Another way is through .NET's Net.NetworkInformation.Ping class. This can be used quickly and with few options, or if you want to control the ping you can look at Net.NetworkInformation.PingOptions.

As an example, here is an old piece of code I put together in the early Powershell v1 days.


function ping-host([string]$server) {
 #This function will perform a simple, small size 
        #single packet ping of a machine and return 
        #true/false for the result
 if ([string]::IsNullOrEmpty($server) ) {return $false}
 #ping first for reachability check
 $po = New-Object net.NetworkInformation.PingOptions
 $po.set_ttl(64)
 $po.set_dontfragment($true)
 [Byte[]] $pingbytes = (65,72,79,89)
 $ping = new-object Net.NetworkInformation.Ping
 $savedEA = $Erroractionpreference
 $ErrorActionPreference = "silentlycontinue"
 $pingres = $ping.send($server, 1000, $pingbytes, $po)
 if (-not $?) {return $false}
 $ErrorActionPreference = $savedEA
 if ($pingres.status -eq "Success") { return $true } else {return $false}
}

This will send a very small ping packet with a 1 sec timeout, hiding any .NET error messages and returning a true or false for availability.



2 comments:

  1. Thanks for this. I've had a ton of trouble with test-connection false positives due to the resources error. the .net class seems to be much more reliable with how it functions. The test-connectivity issue seems to still be an issue even in the latest versions of powershell (3,4)

    ReplyDelete
  2. Thank you much, I also had the trouble with test-connection errors and this worked perfectly.

    ReplyDelete