Pinging a List of Machines in PowerShell


I know what you are thinking, another ping script? Yes, there are thousands of ping tools and scripts out there and every IT administrator has a couple handy that they use on a regular basis. I ended up writing this one because I could not find PowerShell code that met the specific criteria that I needed. I was working with one of my peers on SCCM client health. In large enterprise environments it is an ongoing challenge to ensure that all devices on the network are accounted for and managed. We wrote some PowerShell to query some databases and come up with a list of machines we knew about that were not managed by SCCM. The next question I had was how many were actually on the network.

I wanted my ping code to meet the following criteria.

  • Fast, so pings asynchronously
  • Reliable, can recover and continue if errors are encountered
  • Uses .NET or PS Cmdlets, I don’t want to have to externally launch anything
  • Returns nicely formatted data
  • Is modular, I want to be able to re-use this code in other scripts easily

The examples I found online had most of these features, but not all. Once I started writing it I thought this might be useful to the community so I added the txt file as an input and csv file as an output as my original code was just a few functions I called. Note, this does require PowerShell 3.0 or higher.

Syntax

.\Ping.ps1 –InputFilePath c:\temp\names.txt –MaxConcurrent 100 –TimesToPing 4 –TimeoutInSeconds 90 –ResolveNames true

Parameters

Name Description
InputFilePath Text file with a list of names or IP addresses you want to ping. Each name/IP should be on its own line in the file.
MaxConcurrent How many jobs / threads you want to use for pinging.
TimesToPing How many times you want to ping each name/IP.
TimeoutInSeconds Optional: If some jobs / threads get stuck running, this will ensure that we recover and continue if the timeout is reached. This timeout only applies to Test-Connection (Ping) and the GetHostEntry (DNS) parts of the script. If this is not specified it defaults to 120 seconds.
ResolveNames Optional: Set to true if you want to resolve the names using DNS.

Output

Part 1: This should happen very quickly
Sending Ping Command to 500 Machines… (Uses the Test-Connection Cmdlet with –AsJob)
Getting Ping Results……… (Gets the results from the Cmdlet. The timeout passed in applies to this part of the script. If you see a + here than one of the ping jobs failed and was resubmitted.)
Received Ping Results From 500 Machines

Part 2: This should happen quickly. Note, you can grab more properties from the ping objects in this function if you want. I did notice that getting certain properties cause the function to slow down significantly such as IPv4Address and IPv6Address which are of type System.Net.IPAddress.
Formatting Ping Results…… (Creates an array of objects for the ping results)
Formatted Ping Results for 500 Machines

Part 3: This can take a while
Resolving DNS Names for 500 Machines…*..*…… (GetHostEntry .NET call. The * means MaxConcurrent was hit, which in the case of DNS resolution is hardcoded to 5 since that seemed to work best in my testing.)
Getting DNS Results… (Gets the results of GetHostEntry. The timeout passed is applies to this part of the script. If you see a + here than one of the DNS resolution jobs failed and was resubmitted.)
Received DNS Results From 500 Machines
Formatting DNS Results.. (Adds the DNS information to the array of objects returned from Part 2)
Formatted DNS Results for 500 Machines

Part 4: This should happen quickly, just outputting the results
—Statistics—
Total Time Elapsed: 02 Minutes and 36 Seconds
Total Names Pinged: 500
Hosts that Responded: 250
DNS Names Resolved: 350
Percentage of Hosts that Responded at Least Once: 50%
CSV Output Location: C:\temp\PingResults.csv

Testing

I tested this on two machines using my home internet connection (Verizon FIOS) against 500 internet addresses. If you don’t resolve DNS names then this script finishes very quickly on a fast machine and even faster if you choose to only do 1 ping instead of the normal 4. You can increase the MaxConcurrent setting to get faster results from the ping but if you start seeing a lot of “+” signs in the output then then the jobs are failing (although we should recover from this). Unfortunately because of limitations in the Test-Connection Cmdlet I have to do the DNS resolution and pings separately and the DNS resolution is the longest part of the script. You can use the Win32_PingStatus class directly to do both at the same time, but I’m not sure which method is faster.

ResolveNames = true, MaxConcurrent=100, TimesToPing=4
HP 8570W (i7/32GB), WS2012, Gigabit Ethernet: Completed in 1 minute, 15 seconds.
Microsoft Surface Pro (i5/4GB), Windows8, Gigabit Ethernet: Completed in 2 minutes, 36 seconds.

ResolveNames = false, MaxConcurrent=100, TimesToPing=4
HP 8570W (i7/32GB), WS2012, Gigabit Ethernet: Completed in 19 seconds.
Microsoft Surface Pro (i5/4GB), Windows8, Gigabit Ethernet: Completed in 33 seconds.

Code

1 Param( 2 [parameter(Mandatory=$true)] 3 $InputFilePath, 4 [parameter(Mandatory=$true)] 5 $MaxConcurrent, 6 [parameter(Mandatory=$true)] 7 $TimesToPing, 8 $TimeoutInSeconds, 9 $ResolveNames 10 ) 11 12 $Start = [System.DateTime]::Now 13 Write-Host "Version 1.0" 14 Write-Host "InputFilePath:"$InputFilePath 15 Write-Host "MaxConcurrent:"$MaxConcurrent 16 Write-Host "TimesToPing:"$TimesToPing 17 Write-Host "TimeoutInSeconds:"$TimeoutInSeconds 18 Write-Host "ResolveNames:"$ResolveNames 19 20 function GetNamesFromTextFile 21 { 22 param($file) 23 24 $ht = @{} 25 26 try 27 { 28 foreach ($line in [System.IO.File]::ReadLines($file)) 29 { 30 try { $ht.Add($line.ToString().Trim(), $line.ToString().Trim()) } catch {} 31 } 32 } 33 catch 34 { 35 Write-Host "Failed to Read File, Exiting:"$ms -ForegroundColor Red 36 Write-Host $_.Exception.Message -ForegroundColor Yellow 37 exit 38 } 39 40 return $ht 41 } 42 43 function GetStatusCodeString 44 { 45 param ($code) 46 47 switch ($code) 48 { 49 $null {$ret = "Ping Command Failed"} 50 0 {$ret = "Success"} 51 11001 {$ret = "Buffer Too Small"} 52 11002 {$ret = "Destination Net Unreachable"} 53 11003 {$ret = "Destination Host Unreachable"} 54 11004 {$ret = "Destination Protocol Unreachable"} 55 11005 {$ret = "Destination Port Unreachable"} 56 11006 {$ret = "No Resources"} 57 11007 {$ret = "Bad Option"} 58 11008 {$ret = "Hardware Error"} 59 11009 {$ret = "Packet Too Big"} 60 11010 {$ret = "Request Timed Out"} 61 11011 {$ret = "Bad Request"} 62 11012 {$ret = "Bad Route"} 63 11013 {$ret = "TimeToLive Expired Transit"} 64 11014 {$ret = "TimeToLive Expired Reassembly"} 65 11015 {$ret = "Parameter Problem"} 66 11016 {$ret = "Source Quench"} 67 11017 {$ret = "Option Too Big"} 68 11018 {$ret = "Bad Destination"} 69 11032 {$ret = "Negotiating IPSEC"} 70 11050 {$ret = "General Error"} 71 default {$ret = "Ping Failed"} 72 } 73 74 return $ret 75 } 76 77 function GetPingResultsFromHashTable 78 { 79 param($ht, $maxConcurrent, $count, $timeout) 80 81 $bDone = $false 82 $i = 0 83 $totalMachines = 0 84 $htResults = @{} 85 $dotTime = [System.DateTime]::Now 86 if ($timeout -eq $null) {$timeout = 120} 87 88 Write-Host ("Sending Ping Command to {0} Machines" -f $ht.Count) -NoNewline 89 90 foreach ($name in $ht.GetEnumerator()) 91 { 92 while ((Get-Job -State Running).Count -ge $maxConcurrent) 93 { 94 Start-Sleep -Seconds 1 95 if ($i -ge 50) { Write-Host "*"; $i = 0 } 96 else { Write-Host "*" -NoNewline; $i++ } 97 } 98 99 $job = Test-Connection -ComputerName $name.Key.ToString() -Count $count -AsJob 100 $job.name = "ping:{0}" -f $name.Key.ToString() 101 102 if ([System.DateTime]::Now -gt $dotTime) 103 { 104 $dotTime = ([System.DateTime]::Now).AddSeconds(1) 105 if ($i -ge 50) { Write-Host "."; $i = 0 } 106 else { Write-Host "." -NoNewline; $i++ } 107 } 108 } 109 110 #Start time now, exit in case of timeout 111 $timeout = ([System.DateTime]::Now).AddSeconds($timeout) 112 $dotTime = [System.DateTime]::Now 113 $i = 0 114 Write-Host 115 Write-Host "Getting Ping Results" -NoNewline 116 117 while(!($bDone)) 118 { 119 $results = Get-Job -Name 'ping:*' 120 $bRunning = $false 121 122 foreach ($result in $results) 123 { 124 if ($result.State -ne 'Running') 125 { 126 if ($result.State -eq 'Failed') 127 { 128 #resubmit job 129 if ($i -ge 50) { Write-Host "+"; $i = 0 } 130 else { Write-Host "+" -NoNewline; $i++ } 131 $job = Test-Connection -ComputerName $result.Name.ToString().Split(":")[1] -Count $count -AsJob 132 $job.name = "ping:{0}" -f $result.Name.ToString().Split(":")[1] 133 } 134 else 135 { 136 try { $htResults.Add($result.Name.ToString().Split(":")[1], (Receive-Job $result)) } catch {} 137 $totalMachines++ 138 } 139 140 if ([System.DateTime]::Now -gt $dotTime) 141 { 142 $dotTime = ([System.DateTime]::Now).AddSeconds(1) 143 if ($i -ge 50) { Write-Host "."; $i = 0 } 144 else { Write-Host "." -NoNewline; $i++ } 145 } 146 147 try { Remove-Job $result } catch {} 148 } 149 else 150 { 151 $bRunning = $true 152 } 153 } 154 155 #Check for timeout condition, clean up all jobs if true 156 if ([System.DateTime]::Now -gt $timeout) 157 { 158 $bDone = $true 159 Write-Host "Timeout reached, removing jobs" 160 $results = Get-Job -Name 'ping:*' 161 foreach ($result in $results) 162 { 163 Write-Host "RemoveJob:"$result.Name 164 try 165 { 166 Stop-Job $result 167 try { Remove-Job $result -Force } catch {} 168 } 169 catch {} 170 } 171 } 172 173 #If the timeout hasn't been reached and jobs are still running, loop again 174 if (!($bRunning)) { $bDone = $true } 175 } 176 177 Write-Host 178 Write-Host ("Received Ping Results From {0} Machines" -f $totalMachines) 179 180 return $htResults 181 } 182 183 function ResolveNamesFromPingResults 184 { 185 param($array, $maxConcurrent, $resolveNames, $timeout) 186 187 try { if ($resolveNames -ne $null) { [bool]$resolveNames = [System.Convert]::ToBoolean($resolveNames) } } catch {} 188 189 $htResults = @{} 190 191 if ($resolveNames) 192 { 193 $dotTime = ([System.DateTime]::Now) 194 if ($timeout -eq $null) {$timeout = 120} 195 $i = 0 196 $scriptBlock = 197 { 198 param($s) 199 try { $ret = [System.Net.DNS]::GetHostEntry($s) } catch {} 200 return $ret 201 } 202 Write-Host ("Resolving DNS Names for {0} Machines" -f $array.Count) -NoNewline 203 foreach ($name in $array) 204 { 205 while ((Get-Job -State Running).Count -ge $maxConcurrent) 206 { 207 Start-Sleep -Seconds 1 208 if ($i -ge 50) { Write-Host "*"; $i = 0 } 209 else { Write-Host "*" -NoNewline; $i++ } 210 } 211 $job = Start-Job -ScriptBlock $scriptBlock -ArgumentList $name.NameInList 212 $job.name = "resolve:{0}" -f $name.NameInList 213 if ([System.DateTime]::Now -gt $dotTime) 214 { 215 $dotTime = ([System.DateTime]::Now).AddSeconds(1) 216 if ($i -ge 50) { Write-Host "."; $i = 0 } 217 else { Write-Host "." -NoNewline; $i++ } 218 } 219 } 220 221 #Start time now, exit in case of timeout 222 $timeout = ([System.DateTime]::Now).AddSeconds($timeout) 223 $dotTime = ([System.DateTime]::Now) 224 $i = 0 225 $bDone = $false 226 227 Write-Host 228 Write-Host "Getting DNS Results" -NoNewline 229 while(!($bDone)) 230 { 231 $results = Get-Job -Name 'resolve:*' 232 $bRunning = $false 233 234 foreach ($result in $results) 235 { 236 if ($result.State -ne 'Running') 237 { 238 if ($result.State -eq 'Failed') 239 { 240 #resubmit job 241 if ($i -ge 50) { Write-Host "+"; $i = 0 } 242 else { Write-Host "+" -NoNewline; $i++ } 243 $job = Start-Job -ScriptBlock $scriptBlock -ArgumentList $result.Name.ToString().Split(":")[1] 244 $job.name = "resolve:{0}" -f $result.Name.ToString().Split(":")[1] 245 } 246 else 247 { 248 try { $htResults.Add($result.Name.ToString().Split(":")[1], (Receive-Job $result)) } catch {continue} 249 } 250 251 if ([System.DateTime]::Now -gt $dotTime) 252 { 253 $dotTime = ([System.DateTime]::Now).AddSeconds(1) 254 if ($i -ge 50) { Write-Host "."; $i = 0 } 255 else { Write-Host "." -NoNewline; $i++ } 256 } 257 258 try { Remove-Job $result -Force} catch {} 259 } 260 else 261 { 262 $bRunning = $true 263 } 264 } 265 266 #Check for timeout condition, clean up all jobs if true 267 if ([System.DateTime]::Now -gt $timeout) 268 { 269 $bDone = $true 270 Write-Host "Timeout reached, removing jobs" 271 $results = Get-Job -Name 'resolve:*' 272 foreach ($result in $results) 273 { 274 Write-Host "RemoveJob:"$result.Name 275 try 276 { 277 Stop-Job $result 278 try { Remove-Job $result -Force } catch {} 279 } 280 catch {} 281 } 282 } 283 284 #If the timeout hasn't been reached and jobs are still running, loop again 285 if (!($bRunning)) { $bDone = $true } 286 } 287 Write-Host 288 Write-Host ("Received DNS Results From {0} Machines" -f $htResults.Count) 289 } 290 291 return $htResults 292 } 293 294 function GetFormattedPingResultsFromHashTable 295 { 296 param($ht) 297 298 $fResults = New-Object System.Collections.ArrayList 299 $dotTime = ([System.DateTime]::Now) 300 $i = 0 301 Write-Host "Formatting Ping Results" -NoNewLine 302 303 foreach ($result in $ht.GetEnumerator()) 304 { 305 #There are multiple pings here if we ping more than once per computer 306 $originalAddress = $result.Key.ToString() 307 $pingCount = 0 308 $successCount = 0 309 $status = 'Ping Job Failed' 310 $pingedFrom = 'Ping Job Failed' 311 $successPercentage = 0 312 313 try { $pings = $result.Value.Count } catch { $pings = 0 } 314 if ($pings -gt 0) 315 { 316 $status = GetStatusCodeString -code $result.Value[$pings-1].StatusCode 317 $pingedFrom = $result.Value[$pings-1].PSComputerName 318 } 319 320 foreach ($ping in $result.Value) 321 { 322 $pingCount++ 323 if ($ping.StatusCode -eq 0) { $successCount++ } 324 #If you try to get the IPv4Address or IPv6Address it slows down this loop significantly 325 } 326 327 #Calculate percentage 328 if ($pingCount -ne 0) { $successPercentage = ($successCount / $pingCount) * 100 } 329 else { $successPercentage = 0 } 330 331 #Add to array 332 $o = New-Object PSObject -Property @{ 333 NameInList = $originalAddress 334 PingedFrom = $pingedFrom 335 SuccessPercentage = $successPercentage 336 LastPingStatus = $status 337 } 338 339 [void]$fResults.Add($o) 340 341 if ([System.DateTime]::Now -gt $dotTime) 342 { 343 $dotTime = ([System.DateTime]::Now).AddSeconds(1) 344 if ($i -ge 50) { Write-Host "."; $i = 0 } 345 else { Write-Host "." -NoNewline; $i++ } 346 } 347 } 348 349 Write-Host 350 Write-Host ("Formatted Ping Results for {0} Machines" -f $fResults.Count) 351 352 return $fResults 353 } 354 355 function GetFormattedPingAndDNSResults 356 { 357 param($pingResults, $dnsResults) 358 359 if ($dnsResults.Count -ne 0) 360 { 361 Write-Host "Formatting DNS Results" -NoNewLine 362 $dotTime = ([System.DateTime]::Now) 363 $i = 0 364 foreach ($ping in $pingResults) 365 { 366 $dns = $dnsResults.Get_Item($ping.NameInList) 367 if ($dns -ne $null) 368 { 369 $bFirst = $true 370 foreach ($ip in $dns.AddressList) 371 { 372 if ($bFirst){ $ipList = $ip } 373 else { $ipList += "|" + $ip } 374 } 375 376 $fqdn = $dns.HostName 377 } 378 else 379 { 380 $ipList = $null 381 $fqdn = 'No DNS Entry Found' 382 } 383 384 $ping | Add-Member -MemberType NoteProperty -Name NameFromDNS -value $fqdn -Force 385 $ping | Add-Member -MemberType NoteProperty -Name IPAddressListFromDNS -value $ipList -Force 386 387 if ([System.DateTime]::Now -gt $dotTime) 388 { 389 $dotTime = ([System.DateTime]::Now).AddSeconds(1) 390 if ($i -ge 50) { Write-Host "."; $i = 0 } 391 else { Write-Host "." -NoNewline; $i++ } 392 } 393 } 394 Write-Host 395 Write-Host ("Formatted DNS Results for {0} Machines" -f $pingResults.Count) 396 } 397 398 return $pingResults 399 } 400 401 function GetOutputPath 402 { 403 param($fileName, $dir) 404 $outputPath = $dir + "\" + $fileName 405 return $outputPath 406 } 407 408 function GetTimeSpanStringInMinutesAndSeconds 409 { 410 param($startTime, $endTime) 411 412 $time = $startTime.Subtract($endTime) 413 $minutes = $time.ToString().Split(":")[1] 414 $seconds = $time.ToString().Split(":")[2].Split(".")[0] 415 $timeSpan = "{0} Minutes and {1} Seconds" -f $minutes, $seconds 416 return $timeSpan 417 } 418 419 function GetSuccessPingCount 420 { 421 param($results) 422 423 $successCount = 0 424 foreach ($result in $results) 425 { 426 if ($result.SuccessPercentage -gt 0) { $successCount++ } 427 } 428 429 return $successCount 430 } 431 432 function GetDNSNamesResolvedCount 433 { 434 param($results) 435 436 $namesResolved = 0 437 foreach ($result in $results) 438 { 439 if ($result.IPAddressListFromDNS -ne $null) { $namesResolved++ } 440 } 441 442 return $namesResolved 443 } 444 445 function GetPercentageAsString 446 { 447 param($n1, $n2) 448 449 if ($n1 -ne 0) { $percentage = ($n1 / $n2) * 100 } 450 else { $percentage = 0 } 451 452 $percentage = ("{0:N0}" -f $percentage) + "%" 453 454 return $percentage 455 } 456 457 #Read in Names from text file 458 $Names = GetNamesFromTextFile -file $InputFilePath 459 460 #Get ping results in a hash table. The key is the name and the value is the returned array of ping objects (one element per ping). 461 $Results = GetPingResultsFromHashTable -ht $Names -maxConcurrent $MaxConcurrent -count $TimesToPing -timeout $TimeoutInSeconds 462 463 #Format ping results into an array of objects 464 $FormattedPingResults = GetFormattedPingResultsFromHashTable -ht $Results 465 466 #Resolve DNS Names if specified 467 $DNSResults = ResolveNamesFromPingResults -array $FormattedPingResults -maxConcurrent 5 -resolveNames $ResolveNames -timeout $TimeoutInSeconds 468 469 #Format DNS results by adding them to the ping results 470 $FormattedPingResults = GetFormattedPingAndDNSResults -pingResults $FormattedPingResults -dnsResults $DNSResults 471 472 #Output to CSV 473 $OutputPath = GetOutputPath -fileName 'PingResults.csv' -dir ([Environment]::CurrentDirectory=(Get-Location -PSProvider FileSystem).ProviderPath) 474 try { if ($ResolveNames -ne $null) { [bool]$ResolveNames = [System.Convert]::ToBoolean($ResolveNames) } } catch {} 475 if ($ResolveNames) { $FormattedPingResults | Sort-Object SuccessPercentage | Select-Object NameInList, NameFromDNS, IPAddressListFromDNS, SuccessPercentage, LastPingStatus, PingedFrom | Export-Csv -Path $OutputPath -NoTypeInformation } 476 else { $FormattedPingResults | Sort-Object SuccessPercentage | Select-Object NameInList, SuccessPercentage, LastPingStatus, PingedFrom | Export-Csv -Path $OutputPath -NoTypeInformation } 477 478 #Output Statistics 479 $SuccessPingCount = GetSuccessPingCount -results $FormattedPingResults 480 Write-Host "---Statistics---" -ForegroundColor Green 481 Write-Host ("Total Time Elapsed: " + (GetTimeSpanStringInMinutesAndSeconds -startTime $Start -endTime ([System.DateTime]::Now))) -ForegroundColor Green 482 Write-Host "Total Names Pinged:"$FormattedPingResults.Count -ForegroundColor Green 483 Write-Host ("Hosts that Responded: " + ($SuccessPingCount)) -ForegroundColor Green 484 Write-Host ("DNS Names Resolved: " + (GetDNSNamesResolvedCount -results $FormattedPingResults)) -ForegroundColor Green 485 Write-Host ("Percentage of Hosts that Responded at Least Once: " + (GetPercentageAsString -n1 $SuccessPingCount -n2 $FormattedPingResults.Count)) -ForegroundColor Green 486 Write-Host "CSV Output Location:"$OutputPath -ForegroundColor Yellow

Ping.renametops1


Comments (38)

  1. Reggie says:

    Hi Russ.

    This is such a great script!

    On a weekly basis, I have to ping ~13k-15k machines and it's being done through multiple 1 sheet excel workbooks and matching .vbs scripts. It takes forever!

    We use hostnames in the ping so the DNS resolution part I don't really need, but what I do need is a way to record the IP of any system is successfully pinged. Could you script be modified to report the IP in the results as well?

    I have some programming backgroud in VB.Net and C#.Net, so I understand the basics of the powershell code, but wanted ot see if thre was faster way of making that mod.

    Thanks!

    Rk

  2. Russ Slaten says:

    Thanks Reggie, I'm glad you like the script. If you choose the DNS resolution option it does give you the IP address of the system pinged. There is also another way to get this and when I originally wrote the script I was getting this but in my testing I noticed that on certain IP addresses it would delay the script causing it to take much longer to run. This happened when I was reading properties from the System.Net.IPAddress object. I'm not sure why this occurred but it didn't happen when getting the IP via DNS. If you search the code for "#If you try to get the IPv4Address or IPv6Address it slows down this loop significantly" you'll see the place where this could be added. You'll just need to also include these changes in the formatting sections of the script so it shows up in the csv output. Hope this helps!

  3. Vic Q. says:

    Hi Russ,

    I'm getting the error below when trying to run the script:

    "Method invocation failed because [System.IO.File] doesn't contain a method named 'ReadLines'."

    I have verified the names.txt file exists and the path is correct.

    Can you shed any light on why I would receive this error?

  4. Russ Slaten says:

    Hi Vic,

    It looks like you don't have .NET 4.0. ReadLines() requires it: msdn.microsoft.com/…/dd383503(v=vs.110).aspx. This also means you probably don't have PowerShell 3.0, which requires .NET 4.0: technet.microsoft.com/…/hh847769.aspx.

    Russ

  5. Hi Russ says:

    Thank you very much. That did the job. The script really does work nicely.

  6. Russ Slaten says:

    Glad to hear that worked, thanks!

  7. Lay says:

    Hi Russ,

    Thank you so much. Script work like a champ. Is there a way i can add OS into csv output?

    Any resource would be appriciate..

  8. Russ Slaten says:

    Thanks! Yes, you can add OS to the csv output – but you would need to make some modifications to the script. Check out this post of mine: blogs.msdn.com/…/get-count-of-all-operating-systems-in-a-forest-per-domain.aspx. It uses a cmdlet (that does an LDAP query) to get that information. You can integrate this into the ping script or create a new script that uses this same cmdlet and updates the csv with the information you're after.

  9. Adam says:

    Regarding the ReadLines() problem:

    I got the same issue trying to run this from a remote server, where I didn't have the option to upgrade Powershell or .NET. The solution I used was to edit line 28, replacing it with the following two lines:

       $lines = [System.IO.File]::ReadAllLines($file)

       foreach ($line in $lines)

    Slightly less elegant, but makes the script more universally usable 🙂

    Also, the file "Ping.txt" that you attached to this post is incomplete. It's truncated part way through line 460…

  10. Russ Slaten says:

    Adam, I fixed the attachment, for some reason this blogging platform sometimes truncates txt files so I changed the extension. I appreciate the heads up and the tweak to make it run on older systems. I used that ReadLines() method in another script because the size of the file it was reading was very large, but in this script it makes more sense to use the ReadAllLines() method because there are only so many machines one would want to ping at once. Thanks!

  11. Jonathan says:

    Hey, Russ. Nice script you post here. Needed something like this the other day, did a quick search, and stumbled across your post. Thanks for sharing. Save me some time for sure.

  12. Zafrul says:

    Such a nice script. Beautiful is one word I could think. Elegantly done. I'm a begining with powershell and stumbled across this. Thank you so much for sharing the script.

  13. Russ Slaten says:

    Thanks Jonathan and Zafrul, I'm glad it's useful!

  14. Devon says:

    Thanks for this script, I needed to find out of 260 machines which are actually online

  15. McGuirk says:

    Fantastic script, helped me out a ton, thanks a bunch!

  16. AJ says:

    Script works great but throws an error for me

    Sending Ping Command to 3 Machines.Test-Connection : Cannot validate argument on parameter 'ComputerName'. The argument

    is null or empty. Supply an

    argument that is not null or empty and then try the command again.

    At C:scriptsping_list.ps1:99 char:42

    +     $job = Test-Connection -ComputerName $name.Key.ToString() -Count $count -AsJ …

    +                                          ~~~~~~~~~~~~~~~~~~~~

       + CategoryInfo          : InvalidData: (:) [Test-Connection], ParameterBindingValidationException

       + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.TestConnectionCommand

    Getting Ping Results.

    Received Ping Results From 2 Machines

  17. Russ Slaten says:

    Interesting, it's hard to tell why this happened based just on that error. Do you happen to know what was in the key of the hash table at the time of the error? It's trying to pass the key of a hash table into Test-Connection and failing.

  18. Joseandro says:

    how to display only online hosts?

  19. Russ Slaten says:

    Hi Joseandro,

    You can modify line 475/476 to filter the results based on what you want or since it's outputted to a CSV just open the file in Excel and filter it that way.

    Russ

  20. Sudhir Brahma says:

    A complete script – very well done. Thanks

  21. Ahmed Bouzamondo says:

    Hi Russ,

    Very useful script and well done.

    Thanks

  22. Brandon says:

    Unexpected token 'param' in expression or statement.

    At C:usersbrandon.harperDesktopPing1.ps1:1 char:10

    +   1 Param <<<< (

       + CategoryInfo          : ParserError: (param:String) [], ParseException

       + FullyQualifiedErrorId : UnexpectedToken

    I have no idea what im doing.

  23. Russ Slaten says:

    Brandon, can you tell me how you're calling the script (what arguments you're passing into the script)?

  24. David Cook says:

    Hi Russ,

    When I dump this script into PowerGUI or PowerShell I get an error I can't seem to figure out. The error is as follows when debugging in PowerGUI. The error is highlighted in line 7 denoted by a squiggly line under the $InputFilePath parameter. Can you help? Please run the script in a debugger such as PowerGUI or ISE and send me the results if you can. Thank you, great script!

    Param(            

       [parameter(Mandatory=$true)]            

       $InputFilePath,

       [parameter(Mandatory=$true)]

       $MaxConcurrent,

       [parameter(Mandatory=$true)]

       $TimesToPing,

       [parameter(Mandatory=$true)]    

       $TimeoutInSeconds,

        [parameter(Mandatory=$true)]

       $ResolveNames=$True

       )

    <position> : Missing closing ')' in expression.

       + CategoryInfo          : ParserError: (CloseParenToken:TokenId) [], ParseExceptio

      n

       + FullyQualifiedErrorId : MissingEndParenthesisInExpression

  25. Russ Slaten says:

    David, it works fine for me. If I load the script in PowerShell ISE and start it, I just get prompted for the parameters. Make sure you're downloading the attached file (so no weird characters get pasted from the site).

  26. David Cook says:

    Okay Russ, Sorry I figured that out finally. But now I get this error:

    The script failed due to call depth overflow.  The call depth reached 1011 and the maximum is 1010.

    System.Management.Automation

    How do I get around this?

    Thanks,

    David

  27. Russ Slaten says:

    I haven't run into that, but it looks like a stack overflow type of message. Try reducing the MaxConcurrent setting.

  28. David Cook says:

    OK thank you Russ. When I downloaded the script and renamed. It did not carry over some of the ending expression brackets and the input file had to be surrounded by quotes. Once I did that, it works perfectly although I am not getting any DNS resolution even with extending timeout seconds to 180. Don't know if you have any ideas about the DNS not resolving or not but would like to get that working. I am not pinging machines but rather devices but they are in DNS and can all be resolved via nslookup in our environment.

    Thanks again,

    David

  29. Russ Slaten says:

    Not sure about the DNS, just make sure it is specified in the arguments:  –ResolveNames true

  30. MattMN says:

    Great work. Thank you!

  31. CoryT says:

    Very nice. Not only super useful but educational 🙂

  32. Steve Acx says:

    Hi Russ, this is a great script.  I was wondering if you would share the SCCM scripts you used to check on SCCM clients is they were managed or not.  I am struggling with a issue we have.  We have SCCM 2007 and works good.  WE had a DEV box stood up with SCCM 2012 on it.  It was on a production network.  Another tech working on it checked the box automatically install client on new discovered machines.  So I bet you can guess what happened.  All client were upgraded.  I need to go back to SCCM 2007.  So I am trying to determine what clients are managed and not managed.  I am running a startup script that is uninstalling and re-installing the old clients.  It works sometimes an sometimes not.  So I need to query who is managed and not managed.

  33. Russ Slaten says:

    Hey Steve, I wrote those scripts a really long time ago and they were specific to the SMS 2.0 client (not the advanced client) so I'm afraid they are a bit useless.

  34. Tom H says:

    Great script, thank you for writing and sharing it. Helping me ping machines on our network. And written in my favorite shell to boot! Thanks again.

  35. Kyle M says:

    Thanks for this, i don't have a practical use for this script like many other people posting comments but none the less found it very fascinating to look through and see how it works. Thank you

  36. MattMN says:

    Russ, Thank you for the script. Best solution I have found.

  37. Bret Betnar says:

    Hey Russ, I know this is pretty old. Just wanted to say thanks. It works pretty great. Something I noticed though is; if I set TimesToPing as 1, the $pingedFrom variable always ends up as 'Ping Job Failed'. Setting for 2+ pings works correctly.

    Cheers

Skip to main content