Public/Fast-Ping.ps1

Function Fast-Ping
{
  <#
      .DESCRIPTION
      Super fast ping results for large quantities of hosts.
      .EXAMPLE
      Fast-Ping "myserver.domain.com","yourserver.domain.com"
      .EXAMPLE
      Get-Content C:\test\RandomHosts.txt | Select-Object -First 100 | Fast-Ping
      .NOTES
      Author: Tyson Paul
      Original Author: unknown
      Original script: https://community.idera.com/database-tools/powershell/powertips/b/tips/posts/final-super-fast-ping-command
      I modifed this a bit. I added the ability to determine the max number of hostnames to process without
      choking the Get-WmiObject cmdlet, then recursively probe the chunks of the input hosts array.
  #>


  param
  (
    # make parameter pipeline-aware
    [Parameter(Mandatory,ValueFromPipeline)]

    # one or more computer names (array)
    [string[]]$ComputerName,

    $TimeoutMillisec = 4000
  )

  begin
  {
    [int]$maxParam = 16384
    # use this to collect computer names that were sent via pipeline
    [Collections.ArrayList]$bucket = @()

    # hash table with error code to text translation
    $StatusCode_ReturnValue =
    @{
      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 Failure'
    }


    # hash table with calculated property that translates
    # numeric return value into friendly text

    $statusFriendlyText = @{
      Name       = 'Status'
      Expression = {
        # take status code and use it as index into
        # the hash table with friendly names
        # make sure the key is of same data type (int)
        $StatusCode_ReturnValue[([int]$_.StatusCode)]
      }
    }

    # calculated property that returns $true when status -eq 0
    $IsOnline = @{
      Name       = 'Online'
      Expression = {
        $_.StatusCode -eq 0
      }
    }

    # do DNS resolution when system responds to ping
    $DNSName = @{
      Name       = 'DNSName'
      Expression = {
        if ($_.StatusCode -eq 0)
        {
          if ($_.Address -like '*.*.*.*')
          {
            [Net.DNS]::GetHostByAddress($_.Address).HostName
          }
          else
          {
            [Net.DNS]::GetHostByName($_.Address).HostName
          }
        }
      }
    }
  }

  process
  {
    # add each computer name to the bucket
    # we either receive a string array via parameter, or
    # the process block runs multiple times when computer
    # names are piped
    $ComputerName | ForEach-Object -Process {
      $name = $_ -replace ' ',''
      $null = $bucket.Add($name)
    }
  }

  end
  {
    # Recursively break down query strings that are too long.
    # Assemble the query with all addresses
    $query = $bucket -join "' or Address='"


    If ($query.length -gt $maxParam)
    {
      $tmpQuery = $query.Clone()
      $div = 1
      # If the query is too large, figure out what size chunk of the addresses will be under threshold/max size
      While ($tmpQuery.Length -gt $maxParam)
      {
        $div++
        $tmpQuery = $bucket[0..([math]::Floor(($bucket.Count / $div)))] -join "' or Address='"
      }

      # This size chunk of addresses should be small enough, acceptable as a parameter.
      $start = [math]::Floor(($bucket.Count / $div))
      Write-Verbose -Message "Starting size: $start"

      $index = 0
      For($i = $start; $i -le ($bucket.Count -1); $i = ([math]::Min(($i + $start),($bucket.Count -1))) )
      {
        Write-Verbose -Message "Range:$($index)-$($i)"
        $Result += Fast-Ping -ComputerName $bucket[($index)..$i] -TimeoutMillisec $TimeoutMillisec
        If ($i -eq ($bucket.Count -1))
        {
          break
        }
        $index = $i+1
      }
    }

    Else
    {
      $Result += Get-WmiObject -Class Win32_PingStatus -Filter "(Address='$query') and timeout=$TimeoutMillisec" | Select-Object -Property Address, $DNSName, $IsOnline, $statusFriendlyText
    }

    Return $Result
  }
}