Last week I came across a post on using PowerShell, or more specifically a .NET Framework class, to ping a range of computers in an IP subnet. The original post by Thomas Maurer is here. I added a comment. And after looking at this again I decided to take the ball and run with it a bit further. I'm a big proponent of PowerShell tools that are object oriented and that can add real value to the pipeline. To that end I wrote Test-Subnet.
ManageEngine ADManager Plus - Download Free Trial
Exclusive offer on ADManager Plus for US and UK regions. Claim now!
My function is essentially a wrapper for the Test-Connection cmdlet. By using the cmdlet, I can take advantage of some built in parameters such as the number of pings to send, buffer size and time to live. I'm also going to take advantage of the -Quiet parameter which will return a simple True/False depending on whether the address can be pinged. Here's what the code looks like.
Function Test-Subnet { [cmdletbinding()] Param ( [Parameter(Position=0)] [ValidatePattern("\d{1,3}\.\d{1,3}\.\d{1,3}")] [string]$Subnet="172.16.10", [Parameter(Position=1)] [int[]]$Range=1..254, [int]$Count=1, [int]$Delay=1, [int]$Buffer=32, [int]$TTL=80, [switch]$AsJob ) Write-Verbose "Testing $subnet" #define a scriptblock we can run as a job if necessary $sb={ Param($range,$subnet,$count,$delay,$buffer,$ttl) $range | foreach { $target="$subnet.$_" Write-verbose $target $ping=Test-Connection -ComputerName $target -count $count -Delay $delay -BufferSize $Buffer -TimeToLive $ttl -Quiet New-Object -TypeName PSObject -Property @{ IPAddress=$Target Pinged=$ping TTL=$TTL Buffersize=$buffer Delay=$Delay TestDate=Get-date } } } #close scriptblock if ($AsJob) { Write-Verbose "Creating a background job" Start-Job -ScriptBlock $sb -Name "Ping $subnet" -ArgumentList $range,$subnet,$count,$delay,$buffer,$ttl } else { #run the command normally Invoke-Command -ScriptBlock $sb -ArgumentList $range,$subnet,$count,$delay,$buffer,$ttl } } #end function
This is an advanced function. The download file includes comment based help. The main part of the script takes the range of host numbers and pipes them to a foreach construct.
$range | foreach { $target="$subnet.$_"
Each number in the range is "added" to the subnet to come up with target IP. So if the range is 1..5 and the subnet is 192.16.10 each time through I'll get targets 192.168.10.1 through 192.168.10.5. This address is passed to Test-Connection along with the other values from my function.
$ping=Test-Connection -ComputerName $target -count $count -Delay $delay -BufferSize $Buffer -TimeToLive $ttl -Quiet
Then for each target address, I use the New-Object cmdlet to create a custom object. The -Property parameter is a hash table that will be used as properties for the custom object.
New-Object -TypeName PSObject -Property @{ IPAddress=$Target Pinged=$ping TTL=$TTL Buffersize=$buffer Delay=$Delay TestDate=Get-date }
Most of these values are taken from the function's input values. Although I added a TestDate property to capture the current date and time in the event that you saving your ping results for later review.
The function could have been a simple wrapper but I took it a step further and added my own support to run it as a background job. The Test-Connection cmdlet has an -AsJob parameter. I could have structured my function to use it, but I wanted to show how you might add AsJob support for commands that don't support it natively.
The extra step you need is to turn your core command into a script block. Script blocks can accept parameters which I need to use. Otherwise, the variables wouldn't get resolved because of scope.
$sb={ Param($range,$subnet,$count,$delay,$buffer,$ttl) $range | foreach { $target="$subnet.$_" Write-verbose $target $ping=Test-Connection -ComputerName $target -count $count -Delay $delay -BufferSize $Buffer -TimeToLive $ttl -Quiet New-Object -TypeName PSObject -Property @{ IPAddress=$Target Pinged=$ping TTL=$TTL Buffersize=$buffer Delay=$Delay TestDate=Get-date } } } #close scriptblock
If I don't specify -AsJob, I'll run the script block using Invoke-Command and pass it a list of arguments.
Invoke-Command -ScriptBlock $sb -ArgumentList $range,$subnet,$count,$delay,$buffer,$ttl
But if I use -Asjob, then I'll pass the scriptblock to the Start-Job cmdlet.
if ($AsJob) { Write-Verbose "Creating a background job" Start-Job -ScriptBlock $sb -Name "Ping $subnet" -ArgumentList $range,$subnet,$count,$delay,$buffer,$ttl }
Later I can retrieve the results and then slice and dice as much as I need to. The key takeaway is to always be thinking about objects in the pipeline.
For example, here I'm going to ping 192.168.10.100 through 192.168.10.200 and save the IP addresses that fail to a text file.
PS C:\> Test-Subnet 192.168.10 (100..200) | where {! $_.Pinged} | Select -expand IPAddress | out-file c:\work\ipfail.txt
Or maybe something like this where we pass the IP address to Get-WMIobject.
PS C:\> test-subnet -range (1..10) | where {$_.pinged} | >> foreach {get-wmiobject win32_operatingsystem -comp $_.IPAddress} | >> Select CSName,Caption >>
I trust you get the idea.
Download Test-Subnet and let me know what you think.
UPDATE: A newer version of this function can be found at http://bit.ly/QLDJVX
1 thought on “Ping IP Range”
Comments are closed.