functions/Ping-IpList.ps1

<#
.SYNOPSIS
    Advanced parallel ping utility for multiple IP addresses with history tracking and statistics.
 
.DESCRIPTION
    Ping-IpList provides powerful network connectivity testing capabilities with features like:
    - Parallel ping execution for multiple targets
    - Continuous monitoring mode
    - Ping history visualization
    - DNS resolution
    - Customizable ping parameters
    - Support for IP ranges and CIDR notation
    - Downtime tracking
 
.PARAMETER FromClipBoard
    Reads IP addresses from clipboard
 
.PARAMETER ipList
    Array of IP addresses or hostnames to ping
 
.PARAMETER range
    IP address range in format "192.168.1.1-192.168.1.254"
 
.PARAMETER cidr
    CIDR notation subnet like "192.168.1.0/24"
 
.PARAMETER Count
    Number of ping attempts (default: 4, use -Continuous for endless)
 
.PARAMETER BufferSize
    Size of ping packet in bytes (default: 32)
 
.PARAMETER DontFragment
    Sets the Don't Fragment flag in ping packet
 
.PARAMETER Ttl
    Time to live value (default: 128)
 
.PARAMETER Timeout
    Ping timeout in milliseconds (default: 100)
 
.PARAMETER Continuous
    Enables continuous ping mode
 
.PARAMETER ResolveDNS
    Resolves IP addresses to hostnames
 
.PARAMETER ShowHistory
    Displays ping history using symbols (! for success, . for failure)
 
.PARAMETER HistoryResetCount
    Number of results to keep in history before reset (default: 100)
 
.PARAMETER DontSortIpList
    Prevents automatic IP address sorting
 
.PARAMETER MaxThreads
    Maximum number of concurrent ping threads (default: 100)
 
.PARAMETER OutToPipe
    Outputs results to pipeline instead of console
 
.EXAMPLE
    # Copy these IPs to your clipboard:
    # 192.168.1.10
    # 8.8.8.8
    # 1.1.1.1
    Ping-IpList -FromClipBoard -ShowHistory
 
    Output:
    2024-11-19 14:01:24, Ping sequnce: 1
    192.168.1.10 [t:1ms DownFor:0s]:!
    8.8.8.8 [t:27ms DownFor:0s]:!
    1.1.1.1 [t:18ms DownFor:0s]:!
 
    This example shows how to quickly ping multiple IPs by copying them from any source (text file, Excel, web page).
    Just ensure each IP is on a new line before copying.
 
 
.EXAMPLE
    Ping-IpList -ipList "8.8.8.8","1.1.1.1" -Count 10
 
    Output:
    IPAddress ResponsTime Result DownTime
    --------- ----------- ------ --------
    1.1.1.1 18 Success
    8.8.8.8 27 Success
 
.EXAMPLE
    Ping-IpList -range "192.168.1.1-192.168.1.10" -Continuous -ShowHistory
 
    Output:
    2024-11-19 13:57:24, Ping sequnce: 7
    192.168.1.1 [t:1ms DownFor:0s]:!!!!!!!
    192.168.1.2 [t:-ms DownFor:9s]:.......
    192.168.1.3 [t:-ms DownFor:9s]:.......
    [...]
 
.EXAMPLE
    Ping-IpList -cidr "10.0.0.0/29" -ResolveDNS
 
    Output:
    2024-11-19 13:58:09, Ping sequnce: 4
    IPAddress ResponsTime Result DownTime
    --------- ----------- ------ --------
    10.0.0.0 - TimedOut 4.49
    host1.example.com 1 Success
    10.0.0.2 1 Success
    host3.example.com 1 Success
    [...]
 
.NOTES
    Author: PSNetworking Toolkit
    Requires: PowerShell 5.1 or higher
    Tags: Network, Monitoring, Ping
#>

function Ping-IpList {
   
    [CmdletBinding()]
    param (
        [switch]$FromClipBoard,
        [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('list')]
        [String[]]$ipList,
        [string]$range,
        [string]$cidr,
        [Alias('n')]
        [int]$Count = 4,
        [Alias('l')]
        [int]$BufferSize = 32,
        [Alias('f')]
        [switch]$DontFragment,
        [Alias('i')]
        [int]$Ttl = 128,
        [int]$Timeout = 100,
        [Alias('c','t')]
        [switch]$Continuous,
        [Alias('a')]
        [switch]$ResolveDNS,
        [switch]$ShowHistory,
        [int]$HistoryResetCount = 100,
        [switch]$DontSortIpList,
        [int]$MaxThreads = 100,
        [switch]$OutToPipe
    )
   
    if ($range) {
        $ipList = Get-IpAddressesInRange $range
    }
    elseif ($cidr) {
        $ipList = Get-IPAddressesInSubnet $cidr
    }
    else {
        if ($FromClipBoard) { $ipList = Get-Clipboard }
        # Trim leading or trailing white spaces from each entry in the list
        $ipList = $ipList | ForEach-Object { $_.Trim() }

        # Removing any non-IP addresses or blank entry from the list
        $ipList = $ipList -match "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|^(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$"

        # Check if the list does not contain any hostnames
        if (-not $DontSortIpList) {
            if (($ipList -match "^(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$").Count -eq 0) {
                $ipList = Sort-IpAddress $ipList
            }
        }
    }

    if ($ResolveDNS) {
        for ($i = 0; $i -lt $ipList.Count; $i++) {
            if ($ipList[$i] -match "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}") {
                $NameHost = (Resolve-DnsName $ipList[$i] -Type PTR -DnsOnly -ErrorAction SilentlyContinue).NameHost
                if ($NameHost) { 
                    if ($NameHost.Count -gt 1) {$ipList[$i] = $NameHost[0]} else { $ipList[$i] = $NameHost }
                }
            }
        }
    }

    try {
        if ($Continuous) { $Count = -1 }
        $iCount = 0
        $pingHistory = [ordered]@{} 

        while ($iCount -ne $Count) {
               
            # Create and open a runspace pool
            $pool = [runspacefactory]::CreateRunspacePool(1, $MaxThreads)
            $pool.Open()

            # Collect the runspaces
            $runspaces = New-Object System.Collections.ArrayList

            foreach ($ip in $ipList) {
                # Prepare a script block to run in parallel
                $scriptBlock = {
                    param($ip, $BufferSize, $Ttl, $DontFragment, $Timeout, $HistoryResetCount)
                    function Send-Ping {
                        [CmdletBinding()]
                        param (
                            [string]$target,
                            [int]$BufferSize = 32,
                            [switch]$DontFragment,
                            [int]$Ttl = 128,
                            [int]$Timeout = 100 
                        )
    
                        $ping = New-Object System.Net.NetworkInformation.Ping
                        $pingOptions = New-Object System.Net.NetworkInformation.PingOptions($Ttl, $DontFragment)
                        $pingResult = $ping.Send($target, $Timeout, [System.Text.Encoding]::ASCII.GetBytes(("a" * $BufferSize)), $pingOptions)
                        Return $pingResult
                    }
                    # Simulating a ping result
                    $pingResult = send-ping -Target $ip -BufferSize $BufferSize -Ttl $Ttl -DontFragment:$DontFragment -Timeout $Timeout 

                    $pingStatistics = [pscustomobject]@{
                        IPAddress     = $ip
                        ResponsTime   = [double]0.0
                        Result        = ""
                        ResultHistory = ""
                        DownTimeStart = $null
                        DownTime      = $null
                    }

                    if ($pingResult.Status -eq "Success") {
                        $pingStatistics.ResponsTime = $pingResult.RoundtripTime
                        $pingStatistics.Result = $pingResult.Status
                        $pingStatistics.ResultHistory = "!"
                    }
                    else {
                        $pingStatistics.ResponsTime = "-"
                        $pingStatistics.Result = "TimedOut"
                        $pingStatistics.ResultHistory = "."
                    }
        
                    # Return the result
                    return @($ip, $pingStatistics)
                }

                # Create a new PowerShell runspace and configure it
                $powershell = [powershell]::Create().AddScript($scriptBlock).AddArgument($ip).AddArgument($BufferSize).AddArgument($Ttl).AddArgument($DontFragment).AddArgument($Timeout).AddArgument($HistoryResetCount)
                $powershell.RunspacePool = $pool

                # Start the asynchronous execution of the PowerShell instance
                $runspaces.Add([PSCustomObject]@{
                        Pipe        = $powershell
                        AsyncResult = $powershell.BeginInvoke()
                    }) | Out-Null
            }

            # Collect and process the results as they complete
            foreach ($runspace in $runspaces) {
                $result = $runspace.Pipe.EndInvoke($runspace.AsyncResult)
                $runspace.Pipe.Dispose()

                $ipAddress = $result[0]
                $pingStatistics = $result[1]
               
                if ($pingHistory.Contains($ipAddress)) {
                    if ($pingHistory[$ipAddress].ResultHistory.Length -eq $HistoryResetCount) {
                        $pingHistory[$ipAddress].ResultHistory = ""
                    }
                    $pingHistory[$ipAddress].ResponsTime = $pingStatistics.ResponsTime
                    $pingHistory[$ipAddress].Result = $pingStatistics.Result
                    $pingHistory[$ipAddress].ResultHistory += $pingStatistics.ResultHistory
                }
                else {
                    $pingHistory.Add($ipAddress, $pingStatistics)
                }
               


            }

            # Close and dispose of the runspace pool
            $pool.Close()
            $pool.Dispose()
            
            # Check for DownTime
            foreach ($item in $pingHistory.Values) {
                $timeStamp = Get-Date
                if ($item.Result -eq "TimedOut") {
                    if ($item.DownTimeStart) {
                        $item.DownTime += ($timeStamp - $item.DownTimeStart).TotalSeconds
                        $item.DownTimeStart = $timeStamp
                    }
                    else {
                        $item.DownTimeStart = $timeStamp
                    }
                }
                else {
                    $item.DownTimeStart = $null
                }
            }
            if (-not $OutToPipe) {
            if ($ShowHistory) {
                Clear-Host
                Write-Host "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), Ping sequnce: $($iCount + 1)"
                foreach ($item in $pingHistory.Values) {
                    # $paddingSize = 20 - $item.IPAddress.length
                    # if ($paddingSize -lt 0) { $paddingSize = 0 }
                    Write-Host -ForegroundColor Green -NoNewline "$($item.IPAddress) ["
                    Write-Host -NoNewline "t:"#.PadLeft($paddingSize, " ")
                    Write-Host -ForegroundColor Green -NoNewline "$($item.ResponsTime)ms "
                    Write-Host -NoNewline "DownFor:"
                    Write-Host -ForegroundColor Green -NoNewline "$([math]::Round($item.DownTime,2))s]:"
                    if ($item.ResultHistory.EndsWith("..")) {
                        Write-Host -ForegroundColor Red "$($item.ResultHistory)"
                    }
                    elseif ($item.ResultHistory -like "*.*") {
                        Write-Host -ForegroundColor Yellow "$($item.ResultHistory)"
                    }
                    else {
                        Write-Host "$($item.ResultHistory)"
                    }
                    
                }
            }
            else {
                Clear-Host
                Write-Host "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), Ping sequnce: $($iCount + 1)"
                Write-Output -InputObject $pingHistory.Values | Select-Object IPAddress, ResponsTime, Result, DownTime | Format-Table -RepeatHeader -AutoSize
            }
        }else{
            Write-Host "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), Ping sequnce: $($iCount + 1)"
            if ($iCount -eq ($Count - 1)) {
                Write-Output -InputObject $pingHistory.Values | Select-Object IPAddress, ResponsTime, Result, ResultHistory, DownTime
            }
        }
            $iCount++
            Start-Sleep -Seconds 1
        }

        
        
        
    }
    finally {
       
    }
    
}