Test-PowerPing.psm1

if (!$env:NUMBER_OF_PROCESSORS){
    if ($ismacos){
        $env:NUMBER_OF_PROCESSORS = ((sysctl -a | grep 'machdep.cpu.thread_count') -replace'(.*):','').trim()
    }
    Elseif ($isLinux){
        $env:NUMBER_OF_PROCESSORS = ((lscpu | grep '^CPU(s):') -replace '(.*):','').trim()
    }
}
function get-ppruns {
    $runspace = [PowerShell]::Create()
    $null = $runspace.AddScript($pblock)
    $null = $runspace.AddArgument($time)
    $null = $runspace.AddArgument($NPReply)
    $null = $runspace.AddArgument($ad)
    $null = $runspace.AddArgument($npopt)
    $null = $runspace.AddArgument($pbuffer)
    $null = $runspace.AddArgument($Output)
    $null = $runspace.AddArgument($cnt)
    $null = $runspace.AddArgument($results)
    $null = $runspace.AddArgument($cfnd)
    $null = $runspace.AddArgument($Cpnms)
    $null = $runspace.AddArgument($so)
    $runspace.RunspacePool = $pool
    $null = $runspaces.Add([PSCustomObject]@{Pipe = $runspace; Status = $runspace.BeginInvoke()})
    }
function get-pping {
    $NPReply = (new-object System.Net.NetworkInformation.Ping).SendPingAsync("$ad",$time,$pbuffer,$npopt)
    if ($NPReply.Result.Status -eq 'Success'){
        if ($Output.length -gt '0'){
            if ($Output -eq 'NoTime/DeviceName'){
                $null = $results.$cnt.add([PSCustomObject]@{Status="{0,-11}" -f 'Online'; IPv4Address = if ($ad -like "*.*.*.*"){"{0,-23}" -f "$ad"}Else{"{0,-17}" -f $NPReply.Result.Address.IPAddressToString}})
                }
            elseif ($Output -eq 'NoDeviceName'){
                $null = $results.$cnt.add([PSCustomObject]@{Status="{0,-11}" -f 'Online'; IPv4Address =if ($ad -like "*.*.*.*"){"{0,-23}" -f "$ad"}Else{"{0,-17}" -f $NPReply.Result.Address.IPAddressToString};Time = "{0,-5}" -f $NPReply.Result.RoundtripTime})
                }
            elseif ($Output -eq 'NoTime'){
                $null = $results.$cnt.add([PSCustomObject]@{Status="{0,-11}" -f 'Online'; IPv4Address =if ($ad -like "*.*.*.*"){"{0,-23}" -f "$ad"}Else{"{0,-17}" -f $NPReply.Result.Address.IPAddressToString};DeviceName = if ($ad -like "*.*.*.*"){([Net.DNS]::GetHostByAddress($NPReply.Result.Address.IPAddressToString).HostName)}Else{$ad}})
                }
            }
        Else{
            $null = $results.$cnt.add([PSCustomObject]@{Status="{0,-11}" -f 'Online'; IPv4Address =if ($ad -like "*.*.*.*"){"{0,-23}" -f "$ad"}Else{"{0,-17}" -f $NPReply.Result.Address.IPAddressToString};DeviceName = if ($ad -like "*.*.*.*"){([Net.DNS]::GetHostByAddress($NPReply.Result.Address.IPAddressToString).HostName)}Else{$ad};Time = "{0,-5}" -f $NPReply.Result.RoundtripTime})
            }
        if ($Cpnms){
            if ($Cpnms -contains $results.$cnt.DeviceName){
                $null = $cfnd.add($results.$cnt)
                }
            }
        }
    ELSEif ($so -ne '1' -and $NPReply.Status.value__ -eq '5'){
        if ($Output.length -gt '0'){
            if ($Output -eq 'NoTime/DeviceName'){
                $null = $results.$cnt.add([PSCustomObject]@{Status="{0,-11}" -f $NPReply.Result.Status;IPv4Address = if ($NPReply.Result.Address.IPAddressToString -eq '0.0.0.0'){"{0,-23}" -f ($ad + ": " + $NPReply.Result.Status.value__)}Else{"{0,-23}" -f ($ad + ": " + $NPReply.Result.Status.value__ + ":" + $NPReply.result.Address.IPAddressToString)}})
                }
            elseif ($Output -eq 'NoDeviceName'){
                $null = $results.$cnt.add([PSCustomObject]@{Status="{0,-11}" -f $NPReply.Result.Status;IPv4Address = if ($NPReply.Result.Address.IPAddressToString -eq '0.0.0.0'){"{0,-23}" -f ($ad + ": " + $NPReply.Result.Status.value__)}Else{"{0,-23}" -f ($ad + ": " + $NPReply.Result.Status.value__ + ":" + $NPReply.result.Address.IPAddressToString)}; Time =''})
                }
            elseif ($Output -eq 'NoTime'){
                $null = $results.$cnt.add([PSCustomObject]@{Status="{0,-11}" -f $NPReply.Result.Status;IPv4Address = if ($NPReply.Result.Address.IPAddressToString -eq '0.0.0.0'){"{0,-23}" -f ($ad + ": " + $NPReply.Result.Status.value__)}Else{"{0,-23}" -f ($ad + ": " + $NPReply.Result.Status.value__ + ":" + $NPReply.result.Address.IPAddressToString)}; DeviceName = ''})
                }
            }
        Else{
            $null = $results.$cnt.add([PSCustomObject]@{Status="{0,-11}" -f $NPReply.Result.Status;IPv4Address = if ($NPReply.Result.Address.IPAddressToString -eq '0.0.0.0'){"{0,-23}" -f ($ad + ": " + $NPReply.Result.Status.value__)}Else{"{0,-23}" -f ($ad + ": " + $NPReply.Result.Status.value__ + ":" + $NPReply.result.Address.IPAddressToString)}; DeviceName = ''; Time = ''})
            }
        }
    ELSEif ($NPReply.Status.value__ -ne '5'){
        if ($Output){
            if ($Output -eq 'NoTime/DeviceName'){
                $null = $results.$cnt.add([PSCustomObject]@{Status = "{0,-11}" -f 'Error'; IPv4Address = "{0,-23}" -f ($ad+": "+$NPReply.Exception.InnerException.InnerException.ErrorCode+": "+$NPReply.Exception.InnerException.InnerException.Message)})
                }
            elseif ($Output -eq 'NoDeviceName'){
                $null = $results.$cnt.add([PSCustomObject]@{Status = "{0,-11}" -f 'Error'; IPv4Address = "{0,-23}" -f ($ad+": "+$NPReply.Exception.InnerException.InnerException.ErrorCode+": "+$NPReply.Exception.InnerException.InnerException.Message); Time = ''})
                }
            elseif ($Output -eq 'NoTime'){
                $null = $results.$cnt.add([PSCustomObject]@{Status = "{0,-11}" -f 'Error'; IPv4Address = "{0,-23}" -f ($ad+": "+$NPReply.Exception.InnerException.InnerException.ErrorCode+": "+$NPReply.Exception.InnerException.InnerException.Message); DeviceName = ''; Time = ''})
                }
            }
        Else{
            $null = $results.$cnt.add([PSCustomObject]@{Status = 'Error'; IPv4Address = "{0,-23}" -f ($ad+": "+$NPReply.Exception.InnerException.InnerException.ErrorCode+": "+$NPReply.Exception.InnerException.InnerException.Message); DeviceName = ''; Time = ''})
            }
        }
    $NPReply = $null
    }
<#
.SYNOPSIS
Sends single pings asynchronously to remote computers to determine network accessibility.
Can be used as IP range scanner, ping utility, CIDR calculator, or address list generator.
Returns simple ping results (Status,IPv4Address,DeviceName,Time) with status codes.
.DESCRIPTION
Test-PowerPing pings IPv4 addresses asynchronously within a given IPv4 address range using
PowerShell Runspaces. Ranges can be provided as: Start/EndIP, CIDR, or StartIP with Netmask.
'AddressOnly' switch can be used to calculate IPv4 addresses within a given start/end range.
'IPFilter' parameter can be used to filter IPv4 address lists as needed.
'Sites' parameter set can be used to save IPv4 address ranges, target lists, and CIDR lists.
'CIDR' parameter set can be used to send pings to given CIDR block,
 provide CIDR block information, or generate address lists for given block(s).
'Mask' parameter set can be used to send pings to given range using Start IP and Netmask,
 provide CIDR block information, or generate an address list for the given range.
'Address' parameter set can be used to generate/ping addresses within a given range.
'Targets' parameter set (Default) can be used to send pings to a list of targets.
Defaults:
    Output: Status,IPv4Address,DeviceName,Time (Online & Offline)
                  Timeout: 1000 ms
                      TTL: 255
                   Buffer: 32bytes
 
    Throttle(Threads): Number of Available Processors
    IPFilter: None
    Stream: Disabled
    Address Multithreading (AMT): Disabled
 
 
DYNAMIC PARAMETERS
-Site <String[]>
Name of saved IPv4 address range, CIDR list, or Target list.
Requires valid entry from Site List.
-Use "NewSite" to enable add/remove/import/delete/list functionality
[Command Example: Test-PowerPing -Site NewSite -Import $mylist]
 
Parameter Set(s): Sites
Required? true
Position? named
DefaultValue
Accept pipeline input? true (ByValue)
Accept wildcard characters? false
 
.PARAMETER StartIP
Beginning address in IPv4 address range.
[Command Example: Test-PowerPing -StartIP '10.0.0.0' -Netmask '255.255.255.0']
 
Parameter Set(s): Address,Mask
.PARAMETER EndIP
Ending address in IPv4 address range.
[Command Example: Test-Powerping -StartIP '10.0.0.0' -EndIP '10.0.0.255']
 
Parameter Set(s): Address
.PARAMETER Target
Single device name/IPv4 address, or list of device names/IPv4 addresses to be pinged.
[Command Example: Test-PowerPing -Target 'website.com,www.anothersite.com,10.0.0.223']
 
Parameter Set(s): Targets
.PARAMETER CIDR
Address block entry using CIDR notation
-Minimum MaskBits: 8
[Command Example: Test-PowerPing -CIDR '10.0.0.18/24']
 
Parameter Set(s): CIDR
.PARAMETER Netmask
Netmask for given StartIP
-Minimum Value: 255.0.0.0
[Command Example: Test-PowerPing -StartIP '10.0.0.18' -Netmask '255.255.255.0']
 
Parameter Set(s): Mask
.PARAMETER AddressOnly
Switch to provide IPv4 address list only for Start/EndIP, CIDR Block, or Start with Netmask.
-Multithreading available with AMT Parameter
[Command Example: Test-PowerPing -StartIP '10.0.0.0' -EndIP '10.255.255.255' -AddressOnly]
 
Parameter Set(s): Address,Mask,CIDR,Sites
.PARAMETER CIDcalc
Provides CIDR block information about provided block.
-Available for both CIDR and Netmask entries
[Command Example: Test-PowerPing -CIDR '10.0.0.0/8' -CIDcalc]
 
Parameter Set(s): Mask,CIDR,Sites
.PARAMETER AssignOnly
Only returns/pings assignable addressess for given CIDR or Netmask Range.
[Command Example: Test-PowerPing -StartIP '192.168.20.0' -Netmask '255.255.255.0' -AssignOnly]
 
Parameter Set(s): Mask,CIDR
.PARAMETER AMT
Maximum number of runspaces used to compile address list. See Notes for qualifying ranges.
-Maximum input = $env:NUMBER_OF_PROCESSORS, Minimum input = 1
[Command Example: Test-PowerPing -StartIP '10.0.0.0' -Netmask '255.0.0.0' -AddressOnly -AMT 4]
 
Parameter Set(s): Address,Mask,CIDR,Sites
.PARAMETER Throttle
Maximum number of runspaces used to send asynchronous pings on a given scan.
-Maximum input = 256, Minimum input = 1
[Command Example: Test-PowerPing -CIDR '10.0.0.0/24' -Throttle 64]
 
Parameter Set(s): Address,Mask,CIDR,Sites,Targets
.PARAMETER ComputerName
Filters output to entries where DeviceName resolves to the provided input.
-Enables OnlineOnly parameter when used
[Command Example: Test-PowerPing -Site TheBeach -ComputerName "MyComputerA","MyComputerB"]
 
Parameter Set(s): Address,Mask,CIDR,Sites
.PARAMETER IPFilter
Filters/removes IPv4 address list entries where addresses match the provided input.
-Whole number entries (0-255) will be filtered from the 4th octet for all range entries
-Full addresses can be provided to filter specific IPs ('192.168.50.1')
-Wildcards can be used for pattern filtering ('10.2.*.*')
-Having multiple filters may increase address list calculation time
[Command Example: Test-PowerPing -CIDR 10.0.0.0/14 -AddressOnly -iPFilter '0','10.1.1.2']
 
Parameter Set(s): Address,Mask,CIDR,Sites
.PARAMETER TTL
Sets 'TimeToLive' option property for asynchronous pings.
-Maximum Input = '255'
[Command Example: Test-PowerPing -Target mysite.com -TTL 128]
 
Parameter Set(s): Address,Mask,CIDR,Sites,Targets
.PARAMETER Timeout
Sets response time maximum property for asynchronous pings in milliseconds.
-Maximum Input = '60000'
[Command Example: Test-PowerPing -Target mydevice -Timeout 3000]
 
Parameter Set(s): Address,Mask,CIDR,Sites,Targets
.PARAMETER Buffer
Sets buffer data property for asynchronous pings in bytes.
-Maximim Input = '65500'
[Command Example: Test-PowerPing -Target mydevice -Timeout 3000 -Buffer 64]
 
Parameter Set(s): Address,Mask,CIDR,Sites,Targets
.PARAMETER OnlineOnly
Resulting output will only display pings with a successful response.
[Command Example: Test-PowerPing -CIDR 192.168.50.0/24 -OnlineOnly]
 
Parameter Set(s): Address,Mask,CIDR,Sites,Targets
.PARAMETER Output
Reduces available output properties based on selection.
[Command Example: Test-PowerPing -Site TheBeach -Output NoTime/DeviceName]
 
Parameter Set(s): Address,Mask,CIDR,Sites,Targets
.PARAMETER AddSite
Site Name for addition to Site List. May require admin permissions.
-Can only use one method [AddSite,RemoveSite,Import,ListSites,DeleteSites] per call
[Command Example: Test-PowerPing -Site NewSite -AddSite 'Site' -Range '"10.0.0.0","10.0.0.255"']
 
Parameter Set(s): Sites
.PARAMETER Range
Site Range for addition to Site List. Used with 'AddSite' Parameter. May require admin.
-Must Provide 2 IPv4 addresses (Start & End), separated by commas, contained in single quotes
[Command Example: Test-PowerPing -Site NewSite -AddSite 'Site' -Range '"10.0.0.0","10.0.0.255"']
 
Parameter Set(s): Sites
.PARAMETER nTargetList
List of new Targets for addition to Site List. Used with 'AddSite' Parameter. May require admin.
-Can enter single Target or list of multiple Targets
[Command Example: Test-PowerPing -Site NewSite -AddSite 'Site' -nTargetList "site.com,MYPC"]
 
Parameter Set(s): Sites
.PARAMETER nCIDRList
List of CIDR blocks for addition to Site List. Used with 'AddSite' Parameter. May require admin.
-Can enter single block or list of multiple blocks
-Each entry in list must be quoted as example below
[Command Example: Test-PowerPing -Site NewSite -AddSite 'Site' -nCIDRList "1.0.0.0/8","8.0.0.0/13"]
 
Parameter Set(s): Sites
.PARAMETER RemoveSite
Site Name for removal from Site List. May require admin permissions.
-Can only use one method [AddSite,RemoveSite,Import,ListSites,DeleteSites] per call
[Command Example: Test-PowerPing -Site NewSite -RemoveSite 'TheBeach']
 
Parameter Set(s): Sites
.PARAMETER ListSites
Lists saved Site names and ranges.
-Can only use one method [AddSite,RemoveSite,Import,ListSites,DeleteSites] per call
[Command Example: Test-PowerPing -Site NewSite -ListSites]
 
Parameter Set(s): Sites
.PARAMETER DeleteSites
Deletes Site List CSV and resets Site List. May require admin permissions.
-Can only use one method [AddSite,RemoveSite,Import,ListSites,DeleteSites] per call
[Command Example: Test-PowerPing -Site NewSite -DeleteSites]
 
Parameter Set(s): Sites
.PARAMETER Import
Specifies PSObject or Hashtable to be used to import new entries to Sites list. May require admin.
-Can only use one method [AddSite,RemoveSite,Import,ListSites,DeleteSites] per call
[Command Example: Test-PowerPing -Site NewSite -Import $mysitelist]
 
Parameter Set(s): Sites
.PARAMETER Stream
Enable streaming ping output. Stream count matches Throttle.
[Command Example: Test-PowerPing -Site MySite -Stream]
 
Parameter Set(s): Address,Mask,CIDR,Sites,Targets
.EXAMPLE
Test-PowerPing -StartIP '192.168.20.0' -EndIP '192.168.20.255'
Status IPv4Address DeviceName Time
------ ----------- ---------- ----
TimedOut 192.168.20.0: 11010
Online 192.168.20.1 XT8 1
(...)
____________________________________________________________________
Ping addresses within the range 192.168.20.0 - 192.168.20.255
with 32byte buffer, 1000ms timeout, and 255 ttl.
.EXAMPLE
Test-PowerPing -CIDR 192.168.20.0/23 -Throttle 64 -Timeout '300' -TTL '128' -Buffer '64' -IPFilter "192.168.20.3"
Status IPv4Address DeviceName Time
------ ----------- ---------- ----
TimedOut 192.168.20.2: 11010
TimedOut 192.168.20.4: 11010
(...)
____________________________________________________________________
Ping addresses within the range 192.168.20.0 - 192.168.21.255 with custom ping options,
increased runspaces (Throttle), and an IPFilter. Sends Pings with the provided values for
'Timeout','TTL', and 'Buffer' using a runspace pool with 64 runspaces.
Returns Ping result for successful/unsuccessful addresses that do not equal "192.168.20.3"
.EXAMPLE
Test-PowerPing -Site NewSite -AddSite "MyNewSite" -Range '"192.168.20.0","192.168.20.255"'
********-OR-********
PS C:\> Test-PowerPing -Site NewSite -AddSite "MyNewSitenCIDR" -nCIDRList "192.168.20.0/25","192.168.20.128/25"
********-OR-********
PS C:\> Test-PowerPing -Site NewSite -AddSite "MyNewSitenTarget" -nTargetList "192.168.20.0,192.168.20.1,..."
 
PS C:\> Test-PowerPing -Site NewSite -ListSites
Name Value
---- -----
MyNewSitenCIDR CIDRList,192.168.20.0/25,192.168.20.128/25 (single entry-192.168.20.0/24)
MyNewSite "192.168.20.0","192.168.20.255"
MyNewSitenTarget TargetList,192.168.20.0,192.168.20.1,192.168.20.2,192.168.20.3,...
NewSite "0.0.0.0","0.0.0.0"
____________________________________________________________________
Adds new site 'MyNewSite' to Site List with range '"192.168.20.0","192.168.20.255"'.
**Note Adding/Removing Sites may require admin permissions depending on module install location**
.EXAMPLE
Test-PowerPing -Site NewSite -RemoveSite "MyNewSite"
PS C:\> Test-PowerPing -Site NewSite -ListSites
Name Value
---- -----
MyNewSitenCIDR CIDRList,192.168.20.0/25,192.168.20.128/25
MyNewSitenTarget TargetList,192.168.20.0,192.168.20.1,192.168.20.2,192.168.20.3,...
NewSite "0.0.0.0","0.0.0.0"
____________________________________________________________________
Removes site 'MyNewSite' and corresponding range from Site List.
**Note Adding/Removing Sites may require admin permissions depending on module install location**
.EXAMPLE
Test-PowerPing -Site NewSite -Import $mylist
 
nCIDRList requires first value 'CIDRList' as example above
nTargetList requires first value 'TargetList' as example above
Example List (Hashtable):
$mylist = @{}
$mylist.add("MyNewSite",'"10.0.0.0","10.0.0.255"')
$mylist
Name Value
---- -----
MyNewSite "10.0.0.0","10.0.0.255"
 
--|||--Equivalent Object can also be used for Import--|||---
 
Example .CSV (Object):
$mylist.GetEnumerator() | Select-Object -Property Name,Value | Export-Csv <YourPathHere> -NoTypeInformation
$mycsv = import-csv <YourPathHere>
Test-PowerPing -Site NewSite -Import $mycsv
____________________________________________________________________
Adds list containing entry "MyNewSite" with range "10.0.0.0" - "10.0.0.255" to Site List using import.
.EXAMPLE
Test-PowerPing -Target 'powershellgallery.com,www.powershellgallery.com,mozilla.com,TestComputer'
 
Status IPv4Address DeviceName Time
------ ----------- ---------- ----
Online 20.236.44.162 powershellgallery.com 77
DestinationHostUnreachable www.powershellgallery.com: 11003:40.122.208.145
TimedOut mozilla.com: 11010
Error TestComputer: 11001: No such host is known
____________________________________________________________________
Returns ping results for 4 targets: powershellgallery.com, www.powershellgallery.com, mozilla.com,TestComputer
.EXAMPLE
Test-PowerPing -CIDR 192.168.20.0/24 -CIDcalc
********-OR-********
PS C:\> Test-PowerPing -StartIP '192.168.20.0' -Netmask '255.255.255.0' -CIDcalc
 
CIDR : 192.168.20.0/24
BaseIP : 192.168.20.0
BroadcastIP : 192.168.20.255
AddressCount : 256
FirstAssignable : 192.168.20.1
LastAssignable : 192.168.20.254
AssignableAddresses : 254
Netmask : 255.255.255.0
____________________________________________________________________
Returns CIDR block information for the provided CIDR block, or StartIP with the provided Netmask.
.EXAMPLE
measure-command {$test = test-powerping -CIDR '10.168.50.0/8' -AssignOnly -AddressOnly -AMT 4}
TotalSeconds : 63.4740016
$test.count
16777214
$test[0..-1]
10.0.0.1
10.255.255.254
____________________________________________________________________
Returns a string list containing addressess from 10.0.0.1 to 10.255.255.254 and assigns to $test.
Unassignalbe addresses from the given CIDR block are removed with 'AssignOnly' parameter.
Multithreading used for address generation with 'AMT' parameter, using 4 threads (times may vary).
**Note large address ranges will increase address list calculation time**
**Note pinging or creating large address ranges may max resources or exceed memory resource limitations**
.NOTES
Running Large Range Scans or Address Lists May Increase/Max Resource Utilization
- Using max Throttle (256) may significantly increase memory utilization
- Using multiple filters or large match patterns (10.7.*.*)
            will increase address generation time.
-- Use CIDR parameter with multiple block entries to avoid large filters
 
Qualifying Ranges for AMT
-With AMT enabled, multithreading will be used to calculate address lists
            if the range size meets requirements.
-To run with Multithreading, the 2nd octet must have a difference of 7 or greater.
    -Example Range (does not qualify) '10.0.0.0' - '10.3.255.255'
The range above will be split into 2 sections, each will run in the parent function.
    -Example Range (Minimum) '10.0.0.0' - '10.6.*.*'
The range above will be split into 4 sections equivalent to '10.0.0.0' - '10.1.255.255'
First and last sections will run in parent function, all others will run in runspaces.
 
'Sites' functionality is dependent on saving a .CSV file to Module install directory.
                    Modifying Sites/SiteList May Require Admin Permissions
All Site Modification Switches/Parameters require 'NewSite' selection for 'Site' Parameter
 
Version: 1.0
---------------
Date: 9/16/2023
---------------
Author: Hunter Hirsch
#>

function Test-PowerPing {
[CmdletBinding(DefaultParameterSetName='Targets')]
    Param(
          [Parameter(Mandatory,ParameterSetName='Address',ValueFromPipeline,HelpMessage='Enter Valid IPv4 Address (Ex: "10.0.0.0")')]
          [Parameter(Mandatory,ParameterSetName='Mask',ValueFromPipeline,HelpMessage='Enter Valid IPv4 Address (Ex: "10.0.0.0")')]
          [ValidatePattern('^(([0-1]?[0-9]?[0-9]?|2[0-4][0-9]|25[0-5])\.){3}([0-1]?[0-9]?[0-9]?|2[0-4][0-9]|25[0-5]){1}$')]
          [ValidateLength(7,15)]
          [string]
          $StartIP,
          [Parameter(Mandatory,ParameterSetName="Address",ValueFromPipeline,HelpMessage='Enter Valid IPv4 Address (Ex: "10.0.0.0")')]
          [ValidatePattern('^(([0-1]?[0-9]?[0-9]?|2[0-4][0-9]|25[0-5])\.){3}([0-1]?[0-9]?[0-9]?|2[0-4][0-9]|25[0-5]){1}$')]
          [ValidateLength(7,15)]
          [string]
          $EndIP,
          [Parameter(Mandatory,ParameterSetName="Mask",ValueFromPipeline,HelpMessage='Enter Valid Netmask (Ex: "255.255.255.0")')]
          [ValidateScript({if ($_ -in ('255.0.0.0','255.128.0.0','255.192.0.0','255.224.0.0','255.240.0.0','255.248.0.0','255.252.0.0','255.254.0.0','255.255.0.0','255.255.128.0','255.255.192.0','255.255.224.0','255.255.240.0','255.255.248.0','255.255.252.0','255.255.254.0','255.255.255.0','255.255.255.128','255.255.255.192','255.255.255.224','255.255.255.240','255.255.255.248','255.255.255.252','255.255.255.254','255.255.255.255')){$true}
            Else{
                Throw Write-Output 'Cannot be null. Must be valid Netmask. Subnet Bit Minimum = 8 (255.0.0.0)'
                }
            })]
          [ValidateLength(9,15)]
          [string]
          $Netmask,
          [Parameter(Mandatory,ParameterSetName='CIDR',ValueFromPipeline,HelpMessage='Enter Valid CIDR Range (Ex: "10.0.0.0/24")')]
          [ValidateScript({
            $_ | foreach-object {if ($_ -match '^(([0-1]?[0-9]?[0-9]?|2[0-4][0-9]|25[0-5])\.){3}([0-1]?[0-9]?[0-9]?|2[0-4][0-9]|25[0-5]){1}(\/([8-9]|[1-2][0-9]|3[0-2]))$'){
                 $true
                 }
            Else{
                Throw Write-Output 'Cannot be null. Entries must be enclosed with quotes, then separated by commas. Ex:"10.0.0.0/8","11.0.0.0/12" Subnet Bit Minimum = 8'
                }
            }
            })]
          [string[]]
          $CIDR,
          [Parameter(ValueFromPipeline,ParameterSetName='Sites')]
          [Parameter(ValueFromPipeline,ParameterSetName='CIDR')]
          [Parameter(ValueFromPipeline,ParameterSetName='Mask')]
          [ValidateScript({
            if (!$AssignOnly){
                $true
                }
            Else{
                Throw Write-Output 'Cannot be used with "AssignOnly" Parameter.'
                }
            })]
          [switch]
          $CIDcalc,
          [Parameter(ParameterSetName='CIDR',ValueFromPipeline)]
          [Parameter(ParameterSetName='Mask',ValueFromPipeline)]
          [Parameter(ParameterSetName='Sites',ValueFromPipeline)]
          [ValidateScript({
            if (!$CIDcalc){
                $true
                }
            Else{
                Throw Write-Output 'Cannot be used with "CIDcalc" Parameter.'
                }
            })]
          [switch]
          $AssignOnly,
          [Parameter(ParameterSetName='Sites',ValueFromPipeline)]
          [ValidateScript({
            if(!($RemoveSite)  -and !($DeleteSites) -and !($ListSites) -and !($Import) -and $_ -ne $null){
                $true
                }
            Else{
                Throw Write-Output 'Cannot be null. Cannot be used with RemoveSite,DeleteSites,ListSites,Import Parameters.'
                }
            })]
          [string]
          $AddSite,
          [Parameter(ParameterSetName='Sites',ValueFromPipeline)]
          [ValidateScript({
            if (!($AddSite) -and !($DeleteSites) -and !($ListSites) -and !($Import) -and $_ -ne $null){
                $true
                }
            Else{
                Throw Write-Output 'Cannot be null. Cannot be used with AddSite,DeleteSites,ListSites,Import Parameters.'
                }
            })]
          [string]
          $RemoveSite,
          [Parameter(ParameterSetName='Sites',ValueFromPipeline)]
          [ValidateScript({
            if (!($RemoveSite) -and !($AddSite) -and !($ListSites) -and !($Import)){
                $true
                }
            Else{
                Throw Write-Output 'Cannot be used with AddSite,RemoveSite,ListSites,Import Parameters.'
                }
            })]
          [switch]
          $DeleteSites,
          [Parameter(ParameterSetName='Sites',ValueFromPipeline)]
          [ValidateScript({
            if (!($RemoveSite) -and !($AddSite) -and !($DeleteSites) -and !($Import)){
                $true
                }
            Else{
                Throw Write-Output 'Cannot be used with AddSite,RemoveSite,DeleteSites,Import Parameters.'
                }
            })]
          [switch]
          $ListSites,
          [Parameter(ParameterSetName='Sites',ValueFromPipeline)]
          [ValidateScript({
            if(!($RemoveSite) -and !($DeleteSites) -and !($ListSites) -and !($Import) -and !($nTargetList) -and !($nCIDRList) -and $_ -match '^"(([0-1]?[0-9]?[0-9]?|2[0-4][0-9]|25[0-5])\.){3}([0-1]?[0-9]?[0-9]?|2[0-4][0-9]|25[0-5]){1}","(([0-1]?[0-9]?[0-9]?|2[0-4][0-9]|25[0-5])\.){3}([0-1]?[0-9]?[0-9]?|2[0-4][0-9]|25[0-5]){1}"$' -and $_.split(',')[0] -ne $_.split(',')[1]){
                $true
                }
            Else{
                Throw Write-Output 'Can only be used with "-Site NewSite -AddSite" Parameters. Cannot be null. Must be 2 valid IP addresses. IP Addresses cannot be the same. Type "Get-Help Test-PowerPing -Parameter Range" for further information.'
                }
            })]
          [string]
          $Range,
          [Parameter(ParameterSetName='Sites',ValueFromPipeline)]
          [ValidateScript({
            if (!($AddSite) -and !($DeleteSites) -and !($RemoveSite) -and !($ListSites) -and $_.gettype().name -eq 'PSCustomObject' -or $_.gettype().name -eq 'Hashtable'){
                $true
                }
            Else{
                Throw Write-Output 'Cannot be used with AddSite,RemoveSite,ListSites,DeleteSites Parameters.'
                }
            })]
          [object]
          $Import,
          [Parameter(ParameterSetName='Sites',ValueFromPipeline)]
          [ValidateScript({
            if (!($DeleteSites) -and !($RemoveSite) -and !($ListSites) -and !($Import) -and !($Range) -and !($nTargetList) -and ($_ | foreach-object {$_ -match '^(([0-1]?[0-9]?[0-9]?|2[0-4][0-9]|25[0-5])\.){3}([0-1]?[0-9]?[0-9]?|2[0-4][0-9]|25[0-5]){1}(\/([8-9]|[1-2][0-9]|3[0-2]))$'})){
                $true
                }
            Else{
                Throw Write-Output 'Must be used with AddSite parameter. Cannot be null. Entries must be enclosed with quotes, then separated by commas. Ex:"10.0.0.0/8","11.0.0.0/12" Subnet Bit Minimum = 8'
                }
            })]
          [string[]]
          $nCIDRList,
          [Parameter(ParameterSetName='Sites',ValueFromPipeline)]
          [ValidateScript({
            if (!($DeleteSites) -and !($RemoveSite) -and !($ListSites) -and !($Import) -and !($Range) -and !($nCIDRList) -and $_ -ne $null){
                $true
                }
            Else{
                Throw Write-Output 'Can only be used with "-Site NewSite -AddSite" Parameters. Cannot be used with "Range" Parameter. Cannot be null. Type "Get-Help Test-PowerPing -Parameter nTargetList" for further information.'
                }
            })]
          [String[]]
          $nTargetList,
          [Parameter(Mandatory,ParameterSetName='Targets',ValueFromPipeline,Position=0,HelpMessage='Enter Device Name, Web Address, or IPv4 address.')]
          [ValidateScript({$_ -ne $null})]
          [string[]]
          $Target,
          [Parameter(ValueFromPipeline,ParameterSetName='Sites')]
          [Parameter(ValueFromPipeline,ParameterSetName='Address')]
          [Parameter(ValueFromPipeline,ParameterSetName='CIDR')]
          [Parameter(ValueFromPipeline,ParameterSetName='Mask')]
          [ValidateScript({
            if ($_ -ne $null -and $Output -ne 'NoDeviceName' -and $Output -ne 'NoTime/DeviceName'){
                $true
                }
            Else{
                Throw Write-Output 'Cannot be null. Cannot be used with "Output NoDeviceName" and "Output NoTime/DeviceName" Parameters.'
                }
            })]
          [string[]]
          $ComputerName,
          [Parameter(ValueFromPipeline,ParameterSetName='Sites')]
          [Parameter(ValueFromPipeline,ParameterSetName='Address')]
          [Parameter(ValueFromPipeline,ParameterSetName='CIDR')]
          [Parameter(ValueFromPipeline,ParameterSetName='Mask')]
          [switch]
          $AddressOnly,
          [Parameter(ValueFromPipeline,ParameterSetName='Sites')]
          [Parameter(ValueFromPipeline,ParameterSetName='Address')]
          [Parameter(ValueFromPipeline,ParameterSetName='CIDR')]
          [Parameter(ValueFromPipeline,ParameterSetName='Mask')]
          [ValidateScript({
            if($_ -in (1..[int]$env:NUMBER_OF_PROCESSORS)){
                $true
                }
            Else{
                Throw Write-Output 'Must be an integer between "1" and "$env:Number_of_Processors" (inclusive). Enter "$env:Number_of_Processors" for max performance.'
                }
            })]
          [int]
          $AMT,
          [Parameter(ValueFromPipeline,ParameterSetName='Sites')]
          [Parameter(ValueFromPipeline,ParameterSetName='Address')]
          [Parameter(ValueFromPipeline,ParameterSetName='CIDR')]
          [Parameter(ValueFromPipeline,ParameterSetName='Mask')]
          [ValidateScript({
            if ($_ | foreach-object {if ($_.length -gt '3'){$_ -like '*.*.*.*' -and $_.length -le '15'}Else{$_.length -le '3' -and $_ -notmatch '[a-zA-Z]' -and ($_ -replace '\d','').Length -eq '0'}}){
                $true
                }
            Else{
                Throw Write-Ouput 'Can only filter: End Octets (ex:"0" or "255") IP Addresses (ex:"10.2.3.200") IP Address Patterns (ex:"10.2.*.*") Lists must be quoted and comma separated (ex:"255","10.2.*.*")'
                }
            })]
          [string[]]
          $iPFilter,
          [Parameter(ValueFromPipeline,ParameterSetName='Sites')]
          [Parameter(ValueFromPipeline,ParameterSetName='Address')]
          [Parameter(ValueFromPipeline,Position=3,ParameterSetName='Targets')]
          [Parameter(ValueFromPipeline,ParameterSetName='CIDR')]
          [Parameter(ValueFromPipeline,ParameterSetName='Mask')]
          [ValidateScript({[int]$_ -le '255'})]
          [int]
          $TTL,
          [Parameter(ValueFromPipeline,ParameterSetName='Sites')]
          [Parameter(ValueFromPipeline,ParameterSetName='Address')]
          [Parameter(ValueFromPipeline,Position=1,ParameterSetName='Targets')]
          [Parameter(ValueFromPipeline,ParameterSetName='CIDR')]
          [Parameter(ValueFromPipeline,ParameterSetName='Mask')]
          [ValidateScript({[int]$_ -le '60000'})]
          [int]
          $Timeout,
          [Parameter(ValueFromPipeline,ParameterSetName='Sites')]
          [Parameter(ValueFromPipeline,ParameterSetName='Address')]
          [Parameter(ValueFromPipeline,Position=2,ParameterSetName='Targets')]
          [Parameter(ValueFromPipeline,ParameterSetName='CIDR')]
          [Parameter(ValueFromPipeline,ParameterSetName='Mask')]
          [ValidateScript({[int]$_ -le '65500'})]
          [int]
          $Buffer,
          [Parameter(ValueFromPipeline,ParameterSetName='Sites')]
          [Parameter(ValueFromPipeline,ParameterSetName='Address')]
          [Parameter(ValueFromPipeline,Position=7,ParameterSetName='Targets')]
          [Parameter(ValueFromPipeline,ParameterSetName='CIDR')]
          [Parameter(ValueFromPipeline,ParameterSetName='Mask')]
          [switch]
          $Stream,
          [Parameter(ValueFromPipeline,ParameterSetName='Sites')]
          [Parameter(ValueFromPipeline,ParameterSetName='Address')]
          [Parameter(ValueFromPipeline,Position=6,ParameterSetName='Targets')]
          [Parameter(ValueFromPipeline,ParameterSetName='CIDR')]
          [Parameter(ValueFromPipeline,ParameterSetName='Mask')]
          [ValidateScript({$_ -in (1..256)})]
          [int]
          $Throttle,
          [Parameter(ValueFromPipeline,ParameterSetName='Sites')]
          [Parameter(ValueFromPipeline,ParameterSetName='Address')]
          [Parameter(ValueFromPipeline,Position=4,ParameterSetName='Targets')]
          [Parameter(ValueFromPipeline,ParameterSetName='CIDR')]
          [Parameter(ValueFromPipeline,ParameterSetName='Mask')]
          [switch]
          $OnlineOnly,
          [Parameter(ValueFromPipeline,ParameterSetName='Sites')]
          [Parameter(ValueFromPipeline,ParameterSetName='Address')]
          [Parameter(ValueFromPipeline,Position=5,ParameterSetName='Targets')]
          [Parameter(ValueFromPipeline,ParameterSetName='CIDR')]
          [Parameter(ValueFromPipeline,ParameterSetName='Mask')]
          [ValidateSet('NoTime','NoDeviceName','NoTime/DeviceName')]
          [string]
          $Output
          )
          DynamicParam{
            $pdic = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
            $pcol = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
            $patt = New-Object System.Management.Automation.ParameterAttribute
            $plist = Test-Path ((get-module Test-PowerPing | Select-Object -ExpandProperty ModuleBase) + '\PPSites.csv') -PathType Leaf
            if ($plist){
                $pexp = (get-module Test-PowerPing | Select-Object -ExpandProperty ModuleBase) + '\PPSites.csv' | Import-Csv | Select-Object -ExpandProperty Name
                }
            Else{
                $pexp = 'NewSite'
                }
            $pexps = $pexp -join ','
            $patt.Mandatory = $true
            $patt.HelpMessage = "Enter Site name for existing saved Site, or 'NewSite' to modify Site List. SiteList: $pexps"
            $patt.ParameterSetName = 'Sites'
            $patt.ValueFromPipeline = $true
            $pcol.Add($patt)
            $pvsa = New-Object System.Management.Automation.ValidateSetAttribute(($pexp))
            $pcol.Add($pvsa)
            $RParam = New-Object System.Management.Automation.RuntimeDefinedParameter('Site', [string], $pcol)
            $RParam.Value = 'NewSite'
            $pdic.Add('Site',$RParam)
            return $pdic
          }
begin {
$Site = $PSBoundParameters.Site
Try{
    if ($Site){
        if ($ListSites -or $AddSite -or $RemoveSite -or $DeleteSites -or $Import -or $nTargetList -or $nCIDRList -or $Range -and $Site -ne 'NewSite'){
            $Mant = 'Error: (-Site NewSite) Required to Modify Site List'
            Throw
            }
        $sitep = (get-module -name Test-PowerPing | Select-Object -ExpandProperty ModuleBase) + '\PPSites.csv'
            if (Test-Path -Path $sitep -PathType Leaf){
                $siteimp = Import-Csv $sitep
                $Sites = @{}
                $siteimp | ForEach-Object {$Sites.add($_.name,$_.value)}
                }
            Else{
                $Sites = @{'NewSite' = '"0.0.0.0","0.0.0.0"'}
                }
        if ($Site -eq 'NewSite'){
            if (!($AddSite) -and !($RemoveSite) -and !($DeleteSites) -and !($ListSites) -and !($Import)){
                $Mant = 'Error: No Action Selected for "NewSite"'
                Throw
                }
            if ($ListSites){
                $Sites
                return
                }
            Try{
               $null = new-item -Path ((get-module -name Test-PowerPing | Select-Object -ExpandProperty ModuleBase) + '\accesstest.txt') -ErrorAction Stop
               $null = remove-item -path ((get-module -name Test-PowerPing | Select-Object -ExpandProperty ModuleBase) + '\accesstest.txt') -Force -ErrorAction Stop
                }
            Catch{
                $Mant = '###Error: Access Denied. Must run PowerShell as Admin to modify Sites ###'
                Throw
                }
                if ($AddSite){
                    if ($Sites.Count -ge '5000'){
                        Throw Write-Output '###Error: Sitelist limit reached. Please remove Sites to add New Site ###'
                        }
                    if ((Get-ItemProperty -Path $sitep -Name Length -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Length) -ge '1000000'){
                        Throw Write-Output '###Error: Sitelist size limit reached (1MB). Please remove Sites to add New Site ###'
                        }
                    if ($Sites.Keys -notcontains $AddSite -and !($nTargetList) -and !($nCIDRList)){
                        $AddSite = $AddSite.Trim() -replace '"','' -replace "'",''
                        $Sites.Add("$AddSite","$Range")
                        $Sites.GetEnumerator() | Select-Object -Property Name,Value | Export-Csv $sitep -NoTypeInformation -ErrorAction Stop
                        Write-Output '<<< Update Successful - Module Re-import may be needed>>>'
                        Remove-Item -Path Function:\Test-PowerPing
                        return
                        }
                    Elseif ($Sites.Keys -notcontains $AddSite -and !($Range) -and !($nCIDRList)){
                        $AddSite = $AddSite.Trim() -replace '"','' -replace "'",''
                        if ($nTargetList.Count -gt '1'){
                            if ($nTargetList.count -gt '65536'){
                                Throw Write-Output '###Error: Targetlist too large- Max Entry Count: 65536 ###'
                                }
                            $nTargetList = 'TargetList,' + ($nTargetList -join ',')
                            }
                        Else{
                            if ($nTargetList.Length -gt '795658'){
                                Throw Write-Output '###Error: Targetlist too large- Max Entry Length: 795658 ###'
                                }
                            $nTargetList ='TargetList,' + $nTargetList
                            }
                        $Sites.Add("$AddSite","$nTargetList")
                        $Sites.GetEnumerator() | Select-Object -Property Name,Value | Export-Csv $sitep -NoTypeInformation -ErrorAction Stop
                        Write-Output '<<< Update Successful - Module Re-import may be needed>>>'
                        Remove-Item -Path Function:\Test-PowerPing
                        return
                        }
                    Elseif ($Sites.Keys -notcontains $AddSite -and !($Range) -and !($nTargetList)){
                        $AddSite = $AddSite.Trim() -replace '"','' -replace "'",''
                        if ($nCIDRList.Count -gt '1'){
                            if ($nCIDRList.count -gt '65536'){
                                Throw Write-Output '###Error: CIDRlist too large- Max Entry Count: 65536 ###'
                                }
                            $aCIDRList = 'CIDRList,' + ($nCIDRList -join ',')
                            }
                        Else{
                            $aCIDRList = 'CIDRList,' + ($nCIDRList -join ',')
                            }
                        $Sites.Add("$AddSite","$aCIDRList")
                        $Sites.GetEnumerator() | Select-Object -Property Name,Value | Export-Csv $sitep -NoTypeInformation -ErrorAction Stop
                        Write-Output '<<< Update Successful - Module Re-import may be needed>>>'
                        Remove-Item -Path Function:\Test-PowerPing
                        $aCIDRList = $null
                        return
                        }
                    Else{
                        Write-Output 'Duplicate entry, check value and try again'
                        return
                        }
                    }
            Elseif ($RemoveSite){
                    if ($Sites.Keys -contains $RemoveSite -and $RemoveSite -ne 'NewSite'){
                        $Sites.Remove("$RemoveSite")
                        $Sites.GetEnumerator() | Select-Object -Property Name,Value | Export-Csv $sitep -NoTypeInformation -ErrorAction Stop
                        Write-Output '<<< Update Successful - Module Re-import may be needed >>>'
                        Remove-Item -Path Function:\Test-PowerPing
                        return
                        }
                    Else{
                        Write-Output 'No entry found matching RemoveSite name, check value and try again'
                        return
                        }
                    }
            Elseif ($Import){
                    if ($Import.GetType().name -eq 'Hashtable'){
                            $vtest = $Import.values | ForEach-Object {
                                if ($_ -match '^"(([0-1]?[0-9]?[0-9]?|2[0-4][0-9]|25[0-5])\.){3}([0-1]?[0-9]?[0-9]?|2[0-4][0-9]|25[0-5]){1}","(([0-1]?[0-9]?[0-9]?|2[0-4][0-9]|25[0-5])\.){3}([0-1]?[0-9]?[0-9]?|2[0-4][0-9]|25[0-5]){1}"$'){
                                    $true
                                    }
                                Elseif ($_ -match 'CIDRList,'){
                                    [int]$nccnt = $_.count -1
                                    $cidfl = $_.split(',')[1..$nccnt]
                                    $cidfl | ForEach-Object {
                                        if ($_ -match '^(([0-1]?[0-9]?[0-9]?|2[0-4][0-9]|25[0-5])\.){3}([0-1]?[0-9]?[0-9]?|2[0-4][0-9]|25[0-5]){1}(\/([8-9]|[1-2][0-9]|3[0-2]))$'){
                                            $true
                                            }
                                        Else{
                                            $false
                                            }
                                        }
                                    }
                                Elseif ($_ -match 'TargetList,'){
                                    if ($_.length -le '795658'){
                                        $true
                                        }
                                    }
                                Else{
                                    $false
                                    }
                                }
                            $klist = $Import.Keys | Where-Object {$Sites.Keys -notcontains $_}
                            if ($vtest -notcontains $false -and $klist.count -gt '0'){
                                $klist | ForEach-Object {$Sites.Add("$_",$import[$_])} -ErrorAction Stop
                                $Sites.GetEnumerator() | Select-Object -Property Name,Value | Export-Csv $sitep -NoTypeInformation -ErrorAction Stop
                                Write-Output '<<< Update Successful - Module Re-import may be needed >>>'
                                Remove-Item -Path Function:\Test-PowerPing
                                return
                                }
                            Else{
                                Write-Output 'Bad Import List. Review List for Errors/Duplicates and try again. Enter "Get-Help Test-PowerPing -Examples" for entry syntax (Example#:5).'
                                return
                                }
                        }
                    if ($Import.GetType().name -eq 'Object[]'){
                            $vtest = $Import.value | ForEach-Object {
                                if ($_ -match '^"(([0-1]?[0-9]?[0-9]?|2[0-4][0-9]|25[0-5])\.){3}([0-1]?[0-9]?[0-9]?|2[0-4][0-9]|25[0-5]){1}","(([0-1]?[0-9]?[0-9]?|2[0-4][0-9]|25[0-5])\.){3}([0-1]?[0-9]?[0-9]?|2[0-4][0-9]|25[0-5]){1}"$'){
                                    $true
                                    }
                                Elseif ($_ -match 'CIDRList,'){
                                    [int]$nccnt = $_.count -1
                                    $cidfl = $_.split(',')[1..$nccnt]
                                    $cidfl | ForEach-Object {
                                        if ($_ -match '^(([0-1]?[0-9]?[0-9]?|2[0-4][0-9]|25[0-5])\.){3}([0-1]?[0-9]?[0-9]?|2[0-4][0-9]|25[0-5]){1}(\/([8-9]|[1-2][0-9]|3[0-2]))$'){
                                            $true
                                            }
                                        Else{
                                            $false
                                            }
                                        }
                                    }
                                Elseif ($_ -match 'TargetList,'){
                                    if ($_.length -le '795658'){
                                        $true
                                        }
                                    }
                                Else{
                                    $false
                                    }
                                }
                            $klist = $Import | Where-Object {$Sites.Keys -notcontains $_.name}
                            if ($vtest -notcontains $false -and $klist.count -gt '0'){
                                $klist | ForEach-Object {$Sites.Add($_.name,$_.value)} -ErrorAction Stop
                                $Sites.GetEnumerator() | Select-Object -Property Name,Value | Export-Csv $sitep -NoTypeInformation -ErrorAction Stop
                                Write-Output '<<< Update Successful - Module Re-import may be needed >>>'
                                Remove-Item -Path Function:\Test-PowerPing
                                return
                                }
                            Else{
                                Write-Output 'Bad Import List. Review List for Errors/Duplicates and try again'
                                return
                                }
                        }
                    }
            Elseif ($DeleteSites){
                    if (Test-Path $sitep -PathType Leaf){
                        Remove-Item -Path $sitep -Force -ErrorAction Stop
                        }
                    Write-Output '<<< Update Successful - Module Re-import may be needed >>>'
                    Remove-Item -Path Function:\Test-PowerPing
                    return
                }
        }
        Else{
            if ((($Sites[$Site]) -split ',')[0] -eq 'TargetList'){
                $Target = ($Sites[$Site]) -replace 'TargetList,',''
                }
            Elseif ((($Sites[$Site]) -split ',')[0] -eq 'CIDRList'){
                $CIDR = (($Sites[$Site]) -replace 'CIDRList,','').Split(',')
                }
            Elseif ($Sites[$Site] -match '^"(([0-1]?[0-9]?[0-9]?|2[0-4][0-9]|25[0-5])\.){3}([0-1]?[0-9]?[0-9]?|2[0-4][0-9]|25[0-5]){1}","(([0-1]?[0-9]?[0-9]?|2[0-4][0-9]|25[0-5])\.){3}([0-1]?[0-9]?[0-9]?|2[0-4][0-9]|25[0-5]){1}"$'){
                $StartIP = (($Site | ForEach-Object {$Sites[$_]}) -split ',' -replace '"','')[0]
                $EndIP = (($Site | ForEach-Object {$Sites[$_]}) -split ',' -replace '"','')[1]
                }
            }
        }
    if ($StartIP -and !($Netmask)){
        if ($StartIP -eq $EndIP){
            $Target = $StartIP
            if ($AddressOnly){
                $Target
                return
                }
            }
        Else{
            $rngparam = @{
                Start = $StartIP
                End = $EndIP
                }
            if ($iPFilter){
                $rngparam.Add('Filter',$iPFilter)
                }
            if ($AMT){
                $rngparam.Add('MT',$AMT)
                }
            if ($AddressOnly){
                Get-PowerIPRange @rngparam
                return
                }
            Else{
                $aexp = {Get-PowerIPRange @rngparam}
                }
            }
        }
    if ($Netmask){
        $nl = @{
            '255.0.0.0' = '8'
            '255.128.0.0' = '9'
            '255.192.0.0' = '10'
            '255.224.0.0' = '11'
            '255.240.0.0' = '12'
            '255.248.0.0' = '13'
            '255.252.0.0' = '14'
            '255.254.0.0' = '15'
            '255.255.0.0' = '16'
            '255.255.128.0' = '17'
            '255.255.192.0' = '18'
            '255.255.224.0' = '19'
            '255.255.240.0' = '20'
            '255.255.248.0' = '21'
            '255.255.252.0' = '22'
            '255.255.254.0' = '23'
            '255.255.255.0' = '24'
            '255.255.255.128' = '25'
            '255.255.255.192' = '26'
            '255.255.255.224' = '27'
            '255.255.255.240' = '28'
            '255.255.255.248' = '29'
            '255.255.255.252' = '30'
            '255.255.255.254' = '31'
            '255.255.255.255' = '32'
            }
        $CIDR = $StartIP + '/' + $nl.$Netmask
        }
    if ($CIDR){
        $cidparam = @{
            Range = $CIDR
            }
        if (!$CIDcalc){
            if ($AssignOnly){
                $cidparam.Add('AssignO',$true)
                }
            if ($iPFilter){
                $cidparam.Add('Filter',$iPFilter)
                }
            if ($AMT){
                $cidparam.Add('MT',$AMT)
                }
            if ($AddressOnly){
                Get-PowerIPCIDR @cidparam
                return
                }
            Else{
                $aexp = {Get-PowerIPCIDR @cidparam}
                }
            }
        Else{
            $cidparam.Add('Calculator',$true)
            Get-PowerIPCIDR @cidparam
            return
            }
        }
    if ($Target){
        if ($Target.count -eq '1' -and $Target -match ','){
                $Target = $Target.Split(',')
            }
    }
    if (!($AddressOnly) -and !($CIDcalc) -and !($Mant) -and $Site -ne 'NewSite'){
        if ($Throttle){
            [int]$thrt = $Throttle
            }
        Else{
            [int]$thrt = $env:NUMBER_OF_PROCESSORS
            }
        $powpng = Get-Content function:\get-pping
        $spng = new-object System.Management.Automation.Runspaces.SessionStateFunctionEntry -ArgumentList 'get-pping',$powpng
        $powiss = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault2()
        $powiss.Commands.Add($spng)
        $pool = [RunspaceFactory]::CreateRunspacePool(1,($thrt+1),$powiss,$Host)
        $pool.ApartmentState = 'MTA'
        $pool.ThreadOptions = 'ReuseThread'
        $pool.Open()
        $results = [hashtable]::Synchronized(@{})
        $runspaces = New-Object System.Collections.ArrayList
        $npopt = new-object System.Net.NetworkInformation.PingOptions
        $npopt.Ttl = '255'
        if ($TTL){
            $npopt.Ttl = $TTL
        }
        [int]$buf = '32'
        if ($buffer){
            [int]$buf = $buffer
            }
        $pbuffer = [byte[]]::new($buf)
        $time = '1000'
        if ($Timeout){
            $time = $Timeout
        }
        if ($ComputerName){
            $cfnd = [System.Collections.ArrayList]::Synchronized((New-Object System.Collections.ArrayList))
            if ($ComputerName.count -eq '1' -and $ComputerName -match ','){
                $ComputerName = $ComputerName.Split(',')
                }
            $OnlineOnly = $true
            if (!$Stream){
                $Cpnms = $ComputerName
                }
            }
        Else{
            $Cpnms = $null
            $cfnd = $null
            }
        $NPReply = $null
        [int]$so = '0'
        if ($OnlineOnly){
            $so = '1'
            }
        $pblock = {
            Param(
            $time,
            $NPReply,
            $ad,
            $npopt,
            $pbuffer,
            $output,
            $cnt,
            $results,
            $cfnd,
            $Cpnms,
            $so
            )
            get-pping
            return
            }
        }
    }
Catch {
    if (!($AddressOnly) -and !($CIDcalc) -and $Site -ne 'NewSite' -and !($Mant)){
        $pool.close()
        $pool.dispose()
        }
    if (!($Mant)){
        If ($_.Exception.Message -match "PPSites.csv' is denied" -or $_.Exception.Message -match 'Access to the path is denied'){
            Throw Write-Output '###Error: Access Denied. Must run PowerShell as Admin to modify Sites ###'
            }
        Else{
            Throw $_.Exception.ErrorRecord
            }
        }
    Else{
        Throw Write-Output $Mant
        }
    }
Finally{
    $Sites = $null
    $siteimp = $null
    if ($Netmask){
        $nl = $null
        }
    if ($AddressOnly -or $CIDcalc){
        [System.GC]::Collect()
        }
    }
}

Process {
Try{
if (!($AddressOnly) -and !($CIDcalc) -and $Site -ne 'NewSite'){
    [int]$cnt = 0
    if (!($Stream)){
        if (!($Target)){
            foreach ($ad in (Invoke-Command $aexp)){
                $results.Add($cnt,([System.Collections.ArrayList]::new()))
                get-ppruns
                $cnt++
                }
            }
        Else{
            foreach ($ad in $Target){
                $results.Add($cnt,([System.Collections.ArrayList]::new()))
                get-ppruns
                $cnt++
                }
            }
        while ($runspaces.Status.IsCompleted -contains $false -and !$pool.IsDisposed) {
            if ($ComputerName -and $cfnd){
                if (!(Compare-Object ($cfnd.DeviceName) -DifferenceObject $ComputerName)){
                    $pool.Close()
                    $pool.Dispose()
                    }
                }
            if (!$pool.IsDisposed){
                $runspaces.Where({$_.Status.IsCompleted -eq $true}) | ForEach-Object{
                    $_.Pipe.EndInvoke($_.Status)
                    $_.Pipe.Dispose()
                    $_.Pipe = $null
                    $_.Status = $null
                    $_ = $null
                    }
                }
            Start-Sleep -Milliseconds 10
            }
    }
    Elseif($Stream){
        [int]$si = 0
        [int]$sj = ($thrt) -1
        [int]$sji = $thrt
        $pcnt = [System.Collections.ArrayList]::new()
        if (!($Target)){
            foreach ($ad in (Invoke-Command $aexp)){
                    $results.Add($cnt,([System.Collections.ArrayList]::Synchronized((New-Object System.Collections.ArrayList))))
                    get-ppruns
                    $cnt++
                    }
            }
        Else{
            foreach ($ad in $Target){
                    $results.Add($cnt,([System.Collections.ArrayList]::Synchronized((New-Object System.Collections.ArrayList))))
                    get-ppruns
                    $cnt++
                    }
            }
            while ($runspaces.Status.IsCompleted -contains $false -and !$pool.IsDisposed) {
                $pcmp = ''
                $srg = $si..$sj
                $srg | ForEach-Object {
                    if ($results.$_.count -ne '1'){
                        $pcmp = '1'
                        }
                    }
                    if ($pcmp.Length -eq '0'){
                        if (!$ComputerName){
                            $srg | ForEach-Object {$results.$_}
                            $si = $sj + 1
                            $sj = $sj + $sji
                            $null = $pcnt.Add('1')
                            }
                        Else{
                            $srg | ForEach-Object {if ($ComputerName -contains $results.$_.DeviceName){$null = $cfnd.Add($results.$_); if (!(Compare-Object ($cfnd.DeviceName) -DifferenceObject $ComputerName)){$pool.close();$cfnd;$pool.dispose()}}}
                            $si = $sj + 1
                            $sj = $sj + $sji
                            $null = $pcnt.Add('1')
                            }
                        }
                if (!$pool.IsDisposed){
                    $runspaces.Where({$_.Status.IsCompleted -eq $true}) | ForEach-Object{
                        $_.Pipe.EndInvoke($_.Status)
                        $_.Pipe.Dispose()
                        $_.Pipe = $null
                        $_.Status = $null
                        $_ = $null
                        }
                    }
                Start-Sleep -Milliseconds 10
                }
        if ($thrt -lt $results.Count -and !$pool.IsDisposed){
            [int]$sed = $results.Count -1
            [int]$sdl = $pcnt.Count * $sji
            $srg = $sdl..($sed)
            if ($sdl -lt $sed){
                if (!$ComputerName){
                    $srg | ForEach-Object {$results.$_}
                    }
                Else{
                    $srg | ForEach-Object {if ($ComputerName -contains $results.$_.DeviceName){$null = $cfnd.Add($results.$_); if (!(Compare-Object ($cfnd.DeviceName) -DifferenceObject $ComputerName)){$pool.close();$cfnd;$pool.dispose()}}}
                    }
                }
            }
        $pcnt = $null
        if (!$pool.IsDisposed){
            if ($ComputerName){
                if ($cfnd.count -gt 0){
                    $cfnd
                    }
                Else{
                    Write-Output 'No Devices found matching ComputerName(s)'
                    }
                }
            }
        }
    }
}
Catch{
    $results = $null
    if (!($Mant)){
        Throw $_.Exception.ErrorRecord
        }
    }
Finally{
    if (!($AddressOnly) -and !($CIDcalc) -and $Site -ne 'NewSite' -and !($Mant)){
        $runspaces = $null
        if (!$pool.IsDisposed){
            $pool.Close()
            $pool.Dispose()
            }
        if ($Stream){
            $results = $null
            }
        }
    [System.GC]::Collect()
    }
}
end {
Try{
    If (!($Stream) -and !($AddressOnly) -and !($CIDcalc) -and $Site -ne 'NewSite'){
        if (!($ComputerName)){
            $rng = 0..($results.count -1)
            $rng | ForEach-Object {$results.$_}
            }
        Else{
            if ($cfnd.count -gt 0){
                $cfnd
                }
            Else{
                Write-Output 'No Devices found matching ComputerName(s)'
                }
            $cfnd = $null; $Cpnms = $null
            }
        }
    }
Catch{
    Throw $_.Exception.ErrorRecord
    }
Finally{
    $results = $null; $aexp = $null; $npopt = $null; $NPReply = $null; $ad = $null; $time = $null; $pblock = $null;$pool = $null;$pbuffer = $null;
    if ($Target){
        $Target.Clear()
        }
    if ($ComputerName){
        $ComputerName.Clear()
        }
    if ($CIDR){
        $CIDR.Clear()
        }
    [System.GC]::Collect()
    }
}
}