Super-Fast Network Scans (Part 3)

by Mar 28, 2023

In our previous tips we created two fast functions to ping and port-scan remote systems: Test-Ping and Test-RemotePort:

function Test-RemotePort
{
  param
  (
    [Parameter(Mandatory)]
    [int]
    $Port,
    [string]
    $ComputerName = $env:COMPUTERNAME,
    [int]
    $TimeoutMilliSec = 1000
  )
    
  try
  {
    $client = [Net.Sockets.TcpClient]::new()
    $task = $client.ConnectAsync($ComputerName, $Port)
    if ($task.Wait($TimeoutMilliSec)) { $success = $client.Connected }
    else { $success = $false }
  }
  catch { $success = $false }
  finally
  {
    $client.Close()
    $client.Dispose()
  }
    
  [PSCustomObject]@{
    ComputerName = $ComputerName
    Port = $Port
    Response = $success
  }
}

While both functions are already very fast thanks to their custom timeout, today we’ll launch the turbo mode and perform network scans with lightning speed by parallel processing:

When you do a port scan or a ping, this operation is not dependent on other things so why not speed things up by testing 128 remote systems at the same time rather than doing it one after another?

Windows PowerShell has no parallel looping capability built-in but you can easily add it by installing this module from the PowerShell Gallery:

 
PS C:\> Install-Module -Name PSParallel -Scope CurrentUser 
 

Now compare the speed. Here is the (already fast) sequential scan (make sure you adjust the IP addresses to match your network):

1..255 | 
    ForEach-Object {
    "192.168.2.$_"
    } |
    ForEach-Object {
        Test-Ping -ComputerName $_ -TimeoutMilliSec 500        
    } |
    Select-Object -Property Status, Address, ComputerName

Now we are replacing the busy second ForEach-Object loop by Invoke-Parallel and use its parameter -ThrottleLimit to allow 128 parallel threads:

1..255 | 
    ForEach-Object {
    "192.168.2.$_"
    } |
    Invoke-Parallel {
        Test-Ping -ComputerName $_ -TimeoutMilliSec 500        
    } -ThrottleLimit 128 |
    Select-Object -Property Status, Address, ComputerName |
    Where-Object Status -ne TimedOut

The result appears almost momentarily, and you see just the machines that responded to your ping.

Same for port scans: here is a parallel version that scans the IP range for network printers. Again, make sure you adjust the IP range (and also make sure there *are* network printers available, or change the port number to 445 and search for file servers instead):

1..255 | 
    ForEach-Object {
    "192.168.2.$_"
    } |
    Invoke-Parallel {
        Test-RemotePort -ComputerName $_ -TimeoutMilliSec 500 -Port 9100
    } -ThrottleLimit 128 |
    Select-Object -Property ComputerName, Port, Response |
    Where-Object Response

In PowerShell 7, parallel looping is already built-in so you don’t necessarily need Invoke-Parallel and the module “PSParallel” there.

However, Invoke-Parallel is so much more intuitive and i.e. lets you re-use all the functions you defined in your main thread (for example Test-RemotePort) whereas the PowerShell 7 built-in parallel loop is much more basic and requires you to define a completely new environment for your parallel threads.


Tweet this Tip! Tweet this Tip!