BgPing.ps1 is a Background Ping Script capable of pinging enormous hosts lists in record time. You could call it a Bulk Ping Script, a Mass Ping Script, a Batch Ping Script or even a Mega Ping Script but the bottom line is: this baby will crank through a lot of IP addresses (or hostnames) very quickly.
If you ever have the need to ping dozens, hundreds, or even thousands of hosts, this is the script for you!
This script uses the PowerShell test-connection cmdlet to ping as many hosts as you can throw at it in record time. In my tests, I have pinged over 2,000 hosts in only 26 seconds. How can we do this, you may ask? By leveraging the -asjob parameter, we can launch several ping commands concurrently in the background as PowerShell background jobs. The results are returned amazingly fast!
Please Note: If you write your own script to use test-connection with the -asjob parameter, be aware that there is one significant “gotcha”. In the event that a background job encounters an exception, the entire job fails and no results are returned. Surprised? Me too and that’s certainly not what I was expecting when I started working on this but that’s the way it is. BgPing.ps1 has been coded to gracefully recover from this situation without any data loss. In order to recover from a failed job, BgPing will resubmit a test-connection against each individual host that was in the failed background job. For any exceptions that are found, we will utilize a nonstandard statuscode of “99999″ to which the text “CALL FAILED” will be attached. Without a recovery process such as this the results for the entire batch of hosts in the job would be lost.
The script has numerous options, but there is only ONE required parameter: -HostFile which you must use to pass to the script a list of hostnames (or addresses) to be pinged. As with all of my recent scripts, complete usage help is available via the -Help parameter which I will also expand upon here. This is the standard -Help output from BgPing with additional comments added in green italics:
Usage:
./BgPing -HostFile <filename> [-DnsLookup][-BatchSize <number>][-CheckAfter <number>][-OutFile <filename>][-OutObject][-Help]
Where:
-HostFile <filename>
REQUIRED. Specifies a text file containing a list of network devices to be pinged. List items can be either host names or IP Addresses. Hostnames, or IP Addresses are accepted (IPv4 or IPv6 ok); only ONE ITEM per line. Entries beginning with “#” will be treated as comments and ignored.
-DnsLookup
Causes two additional fields to be added to the results (named “DnsHostName” and “DnsIpAddress”. Both fields will be populated with content if a DNS host lookup is successful, otherwise they will be empty. Note that either entry could be redundant depending on what was passed in the -HostFile entry. Only the first IP Address found in DNS will be returned regardless of how many may be assigned.
CAUTION: Use of this option can add significant processing time.
-BatchSize <number>
Specifies the maximum number of hosts that will be submitted to a background job. Default is 100.
Things to keep in mind: A large batch size means that the overall host listing is split amongst fewer background jobs AND if error recovery becomes necessary, all of the hosts in a failed batch must be resubmitted as individual ping targets. A small batch size will create a greater number of background jobs but may cause a lot of file due to the technique used to split the HostFile listing. If you’re not sure what to do here, just go with the default.
-CheckAfter <number>
Begin checking (and collecting) job results after the the number of active background jobs reaches this number. The default value is 0 (zero) which has the effect of no limit at all whereby job results are not collected until after all background jobs have been submitted.
This parameter was originally designed to alleviate having too many background jobs in memory but after processing many very large host lists with this script, I have not really found it to be necessary. Still, it may be useful if running BgPing on a system with minimal resources.
-OutFile <filename>
Specifies a filename to which the CSV-formatted results will be written. Omitting this parameter will cause the results to be output
as a PowerShell object. See examples for usage.
-OutObject
Causes script to return an array of Ping Result objects which can be captured to a local PowerShell variable for further processing.
This option can be very powerful if you are comfortable working with collections of objects. Use this output if you would like to do any special queries, sorting, or grouping of the output. Note that you can always import the CSV file (from the -OutFile parameter) to work with the collections.
-Show <option>
Specifies which type of ping result to output. Options are:
“FAIL” = Only output non-successful results
“SUCCESS” = Only output successful results
Omit this parameter to show all results
Examples:
./bgping -hostfile servers.txt -batchsize 200 -OutFile pingresult.csv
Results will be written to the the file “pingresult.csv”
$result = ./bgping -hostfile servers.txt -batchsize 200 -OutObject
Results will be assigned to the variable “$result” as a PowerShell object array
Comments: Output from this script will be sorted by hostname with any duplicate entries removed. Source entries (in the -HostFile file) beginning with “#” character will be treated as comments and ignored.
Final note: This script uses colorized output to show progress as it processes your host list. The progress output will be best if you set your PowerShell console window to have a BLACK background. Progress output includes echo of all user parameter settings, elapsed time for major milestones, job submission/retrieval indicators, error detection/recover, and overall results summary.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 | Param ($HostFile, ` [switch]$DnsLookup, [int]$BatchSize=100, ` [int]$CheckAfter=0, $OutFile, ` [switch]$OutObject, $Show="ALL", ` [switch]$Help, [switch]$History) $HelpText = @' ####################################################################################### Name : BgPing.ps1 Date : 03/22/2011 Author : xb90@PoshTips.com Site : http://poshtips.com Purpose : BgPing is a "Background Ping" script designed to quickly check network status of a large list of hostnames (or IP Addresses) using PowerShell background jobs. Under normal conditions the script is capable of returning results for thousands of hosts in less than 10 minutes. Results may be output to a CSV file or returned as a PowerShell object. Usage: ./BgPing -HostFile [-DnsLookup][-BatchSize ][-CheckAfter ][-OutFile ][-OutObject][-Help] Where: -HostFile Specifies a text file containing a list of network devices to be pinged. List items can be either host names or IP Addresses. Hostnames, or IP Addresses are accepted (IPv4 or IPv6 ok); only ONE ITEM per line. Entries beginning with "#" will be treated as comments and ignored. -DnsLookup Causes two additional fields to be added to the results (named "DnsHostName" and "DnsIpAddress". Both fields will be populated with content if a DNS host lookup is successful, otherwise they will be empty. Note that either entry could be redundant depending on what was passed in the -HostFile entry. Only the first IP Address found in DNS will be returned regardless of how many may be assigned. CAUTION: Use of this option can add significant processing time. -BatchSize Specifies the maximum number of hosts that will be submitted to a background job. Default is 100. -CheckAfter Begin checking (and collecting) job results after the the number of active background jobs reaches this number. The default value is 0 (zero) which has the effect of no limit at all whereby job results are not collected until after all background jobs have been submitted. -OutFile Specifies a filename to which the CSV-formatted results will be written. Omitting this parameter will cause the results to be output as a PowerShell object. See examples for usage. -OutObject Causes script to return an array of Ping Result objects which can be captured to a local PowerShell variable for further processing. -Show Specifies which type of ping result to output. Options are: "FAIL" = Only output non-successful results "SUCCESS" = Only output successful results Omit this parameter to show all results -Help Displays this help Examples: ./bgping -hostfile servers.txt -batchsize 200 -OutFile pingresult.csv Results will be written to the the file "pingresult.csv" $result = ./bgping -hostfile servers.txt -batchsize 200 -OutObject Results will be assigned to the variable "$result" as a PowerShell object array Comments: Output from this script will be sorted by hostname with any duplicate entries removed. Source entries (in the -HostFile file) beginning with "#" character will be treated as comments and ignored. ####################################################################################### '@ $HistoryText = @' Maintenance Log Date By Updates (insert newest updates at top) ---------- ---- --------------------------------------------------------------------- 04/01/2011 xb90 Bugfix - force statuscode to 99999 for non-resolvable hostnames 03/28/2011 xb90 New Script ####################################################################################### '@ #Note: Status Code 99999 has been added to flag test-connectin call failures $StatusCodes = @{ 0 = "Success"; 11001 = "Buffer Too Small"; 11002 = "Destination Net Unreachable"; 11003 = "Destination Host Unreachable"; 11004 = "Destination Protocol Unreachable"; 11005 = "Destination Port Unreachable"; 11006 = "No Resources"; 11007 = "Bad Option"; 11008 = "Hardware Error"; 11009 = "Packet Too Big"; 11010 = "Request Timed Out"; 11011 = "Bad Request"; 11012 = "Bad Route"; 11013 = "TimeToLive Expired Transit"; 11014 = "TimeToLive Expired Reassembly"; 11015 = "Parameter Problem"; 11016 = "Source Quench"; 11017 = "Option Too Big"; 11018 = "Bad Destination"; 11032 = "Negotiating IPSEC"; 11050 = "General Error has occurred"; 99999 = "CALL FAILED"} if ($help -or $History -or (!$HostFile)){ write-host $HelpText if ($History){write-host $HistoryText} exit } function isIPAddress(){ param ($object) ($object -as [System.Net.IPAddress]).IPAddressToString -eq $object -and $object -ne $null } ###################################################### ## MAIN ###################################################### $elapsedTime = [system.diagnostics.stopwatch]::StartNew() $result = @() $itemCount = 0 $callFailure = test-connection -computername localhost -count 1 $callFailure.statuscode = 99999 $callFailure.address = "notset" if (get-job|? {$_.name -like "BgPing*"}){ write-host "ERROR: There are pending background jobs in this session:" -back red -fore white get-job |? {$_.name -like "BgPing*"} | out-host write-host "REQUIRED ACTION: Remove the jobs and restart this script" -back black -fore yellow $yn = read-host "Automatically remove jobs now?" if ($yn -eq "y"){ get-job|? {$_.name -like "BgPing*"}|% {remove-job $_} write-host "jobs have been removed; please restart the script" -back black -fore green } exit } if (!(test-path $HostFile)){ write-host "ERROR: `"$HostFile`" is not a valid file" -back black -fore red write-host "REQUIRED ACTION: Re-run this script using a valid filename" -back red -fore white exit } $offset = 0 $itemCount = gc $HostFile |sort |get-unique | ? {((!$_.startswith("#")) -and ($_ -ne ""))} | measure-object -line |% {$_.lines} write-host " BgPing started at $(get-date) ".padright(60) -back darkgreen -fore white write-host " -HostFile : $HostFile" -back black -fore green write-host " (contains $itemCount unique entries)" -back black -fore green if ($DnsLookup){$temp="Selected"}else{$temp="Not Selected"} write-host " -DnsLookup : $temp" -back black -fore green write-host " -BatchSize : $BatchSize" -back black -fore green if ($CheckAfter){$temp="Selected"}else{$temp="Not Selected"} write-host " -CheckAfter : $temp" -back black -fore green write-host " -Show : $Show" -back black -fore green if ($OutFile){$temp=$OutFile}else{$temp="Not Selected"} write-host " -OutFile : $temp" -back black -fore green if ($OutObject){$temp="Selected"}else{$temp="Not Selected"} write-host " -OutObject : $temp" -back black -fore green write-host "" $activeJobCount = 0 $totalJobCount = 0 write-host "Submitting background ping jobs..." -back black -fore yellow for ($offset=0; $offset -lt $itemCount;$offset += $batchSize){ $activeJobCount += 1; $totalJobCount += 1; $HostList = @() $HostList += gc $HostFile |sort |get-unique |? {((!$_.startswith("#")) -and ($_ -ne ""))} | select -skip $offset -first $batchsize $j = test-connection -computername $HostList -count 4 -throttlelimit 32 -erroraction silentlycontinue -asjob # Initial Job Name Format: BgPing:N:N:N # where numeric placeholders (N) = # JobCount # Starting Line to begin reading from HostFile # Number of Lines read from hostfile $j.name = "BgPing`:$totalJobCount`:$($offset+1)`:$($HostList.count)" #write-host "$($j.name)" write-host "+" -back black -fore cyan -nonewline if (($checkAfter) -and ($activeJobCount -ge $checkAfter)){ write-host "`n$totaljobCount jobs submitted; checking for completed jobs..." -back black -fore yellow foreach ($j in get-job | ? {$_.name -like "BgPing*" -and $_.state -eq "completed"}){ $result += receive-job $j remove-job $j $activeJobcount -= 1 write-host "-" -back black -fore cyan -nonewline } } } write-host "`n$totaljobCount jobs submitted, checking for completed jobs..." -back black -fore yellow $recCnt = 0 while (get-job |? {$_.name -like "BgPing*"}){ foreach ($j in get-job | ? {$_.name -like "BgPing*"}){ #write-host "Job: $($j.name) State: $($j.state) " -back black -fore cyan -nonewline $temp = @() if ($j.state -eq "completed"){ $temp = @() $temp += receive-job $j $result += $temp #write-host " (read $($temp.count) Lines - result count : $($result.count))" -back black -fore green remove-job $j $ActiveJobCount -= 1 write-host "-" -back black -fore cyan -nonewline } elseif ($j.state -eq "failed"){ $temp = $j.name.split(":") if ($temp[1] -eq "R"){ # # This is a single-entry recovery Job failure # extract hostname from the JobName and update our callFailure record # force-feed callFailure record into the results array # delete the job # write-host " " write-host "Call Failure on Host: $($temp[2]) " -back red -fore white remove-job $j $callFailure.address = $temp[2] $result += $callFailure write-host "resuming check for completed jobs..." -back black -fore yellow } else{ # # The original background job failed, so need to resubmit each hostname from that job # to determine which host failed and gather accurate ping results for the others # # Recovery Job Name Format: BgPing:R: # where "R" indicates a Recovery job and is the hostname or IP Address to be pinged # Recovery jobs will only have ONE hostname specified # write-host "`nFailure detected in job: $($j.name); recovering now..." -back black -fore red remove-job $j $ActiveJobCount -= 1 $HostList = gc $HostFile |sort|get-unique|? {((!$_.startswith("#")) -and ($_ -ne ""))} | select -skip $($temp[2]-1) -first $temp[3] foreach ($x in $HostList){ $j = test-connection -computername $x -count 4 -throttlelimit 32 -erroraction silentlycontinue -asjob $j.name = "BgPing:R:$x" write-host "." -back black -fore cyan -nonewline } write-host "`nresuming check for completed jobs..." -back black -fore yellow } } } if ($result.count -lt $itemCount){ sleep 5 } } write-host " " write-host " BgPing finished Pinging at $(get-date) ".padright(60) -back darkgreen -fore white write-host (" Hosts Pinged : {0}" -f $($result.count)) -back black -fore green write-host (" Elapsed Time : {0}" -f $($ElapsedTime.Elapsed.ToString())) -back black -fore green $result | add-member -membertype NoteProperty -Name StatusDescr -value "" -Force foreach ($r in $result){ if ($r.statusCode -eq $null){ $r.statusCode = 99999 } $r.StatusDescr = $statusCodes.item([int]$r.statusCode) } if ($DnsLookup){ write-host "DNS Hostname lookup started..." -back black -fore yellow write-host "BE PATIENT - this could take a while!" -back black -fore yellow $result | add-member -membertype noteproperty -name DnsHostName -value "" -Force $result | add-member -membertype noteproperty -name DnsIpAddress -value "" -Force $DnsLookupFailures = 0 foreach ($r in $result){ try{ # gethostentry only thows errors on invalid IP Addresses # if returned hostname is IpAddress, then the lookup failed $x = [system.net.dns]::gethostentry($r.address) if (isIPAddress $x.hostname){ throw "no such host is known" } else{ $r.DnsHostName = $x.hostname $r.DnsIpAddress = $x.addresslist[0].ipaddresstostring } write-host "." -back black -fore cyan -nonewline } catch{ write-host "." -back red -fore black -nonewline #write-host "`nDNS Lookup Failed: $($r.address)" -back red -fore black -nonewline $DnsLookupFailures += 1 } } write-host "" write-host " BgPing finished DNS Lookup at $(get-date) ".padright(60) -back darkgreen -fore white write-host (" Lookup Failures : {0}" -f $DnsLookupFailures) -back black -fore green write-host (" Lookup Success : {0}" -f ($itemCount - $DnsLookupFailures)) -back black -fore green write-host (" Elapsed Time : {0}" -f $($ElapsedTime.Elapsed.ToString())) -back black -fore green } write-host "`nSUMMARY:" -back black -fore cyan $result | group statuscode | sort count | ` ft -auto ` @{Label="Status"; Alignment="left"; Expression={"$($_.name)"}}, ` @{Label="Description"; Alignment="left"; Expression={"{0}" -f $statuscodes.item([int]$_.name)}}, ` count ` | out-host switch ($show){ SUCCESS{ write-host "extracting Successful ping results..." -back black -fore yellow $result = $result|? {$_.statuscode -eq 0} write-host "done" -back black -fore yellow write-host (" Elapsed Time : {0}" -f $($ElapsedTime.Elapsed.ToString())) -back black -fore green } FAIL{ write-host "extracting UnSuccessful ping results..." -back black -fore yellow $result = $result|? {$_.statuscode -ne 0} write-host "done" -back black -fore yellow write-host (" Elapsed Time : {0}" -f $($ElapsedTime.Elapsed.ToString())) -back black -fore green } } if ($OutFile){ write-host "writing results to $outfile... " -back black -fore yellow if ($DnsLookup){ $result | select address,statuscode,statusdescr,dnshostname,dnsipaddress | export-csv -notypeinfo -path $OutFile } else{ $result | select address,statuscode,statusdescr | export-csv -notypeinfo -path $OutFile } write-host "done" -back black -fore yellow } if ($OutObject){ $result } write-host " BgPing completed all requested operations at $(get-date) ".padright(60) -back darkgreen -fore white write-host (" Elapsed Time : {0}" -f $($ElapsedTime.Elapsed.ToString())) -back black -fore green |



Fixed bug: needed to force statuscode to 99999 for non-resolvable hostnames
Test-Connection returns a StatusCode of NULL for non-resolvable hostnames. When this Null value was cast to an [int] it became a Zero and that was then used for looking up corresponding status code description in the $StatusCodes hash table. This caused an erroneous “success” description to be reported when in reality it was throwing an exception.
Issue fixed
Note: Updates log can be viewed by using the -History switch
e.g.
./bgPing -History
Great script. You can even shave off another couple of seconds (tested against 1500 servers) by parsing -HostFile only once into an array , as well as using Sort -Unique instead of piping Sort to Get-Unique.
Outstanding – thank you.
I have about 3600 end hosts to ping and whilst I have only tested this on 255 hosts (7 seconds!) I imagine this will help tremendously.
Thanks again.
Jason
Download link is broken.
Thanks Björn.
Looks like the code display plugin that I’m using is broken. I’ve disabled the “download” feature so (until I can find a fix) people will need click “View Code” and then cut-and-paste into a text editor.
Great scripts….
Thanks a million.
Love it! Thanks, this is exactly what I was looking for!
The script seems to be broken .Can you please take a look.
Hey..it worked fine
… Thanks a ton.. Please ignore my earlier post
is it possible to list out the server name which is RTO and also for the servers which is host unreachable.
If you want to list only the unreachable hosts (or any other status) you can use the “-OutObject” switch to capture the results to a powershell variable OR use -outfile to capture the results to a CSV file. Once the results are captured, you can select and view the data based on any criteria.
-outobject method:
$x = ./bgping.ps1 -hostfile myhostlist.txt -outobject$x | ? {$_.statusdescr -like "*unreachable*"} | select address
-outfile method:
./bgping.ps1 -hostfile myhostlist.txt -outfile pingresults.csvipcsv pingresults.csv | ? {$_.statusdescr -like "*unreachable*"} | select address
Not sure what you mean by “RTO” but hopefully this will help to zero in on it.
I added a little more so that I could get an email of failed pings. Does this look like it will return any servers that are not responding.
$users = “dczarny@test.com” # List of users to email your report to (separate by comma)
$fromemail = “dczarny@test.com”
$server = “test.com” #enter your own SMTP server DNS name / IP address here
$tableFragment = “”
$results = ./..\”SQL Server”\”Powershell Monitoring”\bgping -hostfile ..\”SQL Server”\”Powershell Monitoring”\servers.txt -batchsize 200 -OutObject -show “FAIL”
foreach ($result in $results)
#{Write-Host $result.Address}
{$tableFragment = $tableFragment + “” + $result.Address + “” }
#Write-Host $tableFragment
if($results)
{
# assemble the HTML for our body of the email report.
$HTMLmessage = @”
Server(s) Not Responding to Pings
This email was generated because the following server(s) are not responding to pings.
body{font: .8em “”Lucida Grande”", Tahoma, Arial, Helvetica, sans-serif;}
ol{margin:0;padding: 0 1.5em;}
table{color:#FFF;background:#C00;border-collapse:collapse;width:100px;border:5px solid #900;}
thead{}
thead th{padding:1em 1em .5em;border-bottom:1px dotted #FFF;font-size:120%;text-align:left;}
thead tr{}
td{padding:.25em .5em;}
tfoot{}
tfoot td{padding-bottom:1.5em;}
tfoot tr{}
#middle{background-color:#900;}
HTML TABLE
__SERVERS__
$tableFragment
“@
$regexsubject = $HTMLmessage
$regex = [regex] ‘(?im)’
if ($regex.IsMatch($regexsubject))
{send-mailmessage -from $fromemail -to $users -subject “Ping Server Report” -BodyAsHTML -body $HTMLmessage -priority High -smtpServer $server}
I can’t get this script to work with a list I create with the Get-QADComputer Commandlet. (All failed with 99999)
Can any of the readers give me a working example of this approach?
I am not at all familiar with powergui cmdlets, but I’ll bet that the list you are generating is not in a standard text format. I suggest that you generate your host list with output to a text file so that you can inspect the contents and make sure that you have only host names (1 per line) and no extraneous characters.
Hope this helps…
The problem were the spaces that were written to the textfile created with the out-file command (I don’t have a clue why). If you delete them after creating the hosts file BgPing works.
Hello, Tom.
Here’s, I think the solution
$hostlist = (Get-ADComputer -Searchbase “OU=Computers, dc=domainname, dc=com” -f * |% {$_.name}) | select -skip $offset -first $batchsize
Is there any way to force the -DNSLookUp piece to use IPv4 when writing the IP address instead of writing MAC address?
I’m guessing that your DNS infrastructure will determine whether you get ipV6 or ipV4 results from the GetHostEntry method call. If anyone else is more savvy on this, feel free to post a comment.
Thanks!