Port scans can be useful to identify devices or machines in your network, i.e. file servers (port 445), PowerShell remoting targets (port 5985), or network printers (port 9100).
However, when you try and scan a network segment with the built-in PowerShell cmdlets, this is annoyingly slow. Here is an example that scans for network printers on port 9100 (make sure you adjust the network range to fit your network):
1..255 | ForEach-Object { "192.168.2.$_" } | ForEach-Object { Test-NetConnection -ComputerName $_ -Port 9100 } | Select-Object -Property ComputerName, RemotePort, PingSucceeded, TcpTestSucceeded
The code takes up to 30 seconds (per IP address). The reason for this is primarily due to the fact that Test-NetConnection has no parameter to specify a timeout. So when there is no answer for a given IP address, the cmdlet waits forever until it decides that this machine must be offline.
In part 1 of this series, we add dramatic speed improvements by replacing Test-NetConnection with a faster alternative that does have a timeout:
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 } }
By exchanging the functions in our initial script (and adjusting the Select-Object properties), the range port scan is now much faster and takes only half a second per IP address:
1..255 | ForEach-Object { "192.168.2.$_" } | ForEach-Object { Test-RemotePort -ComputerName $_ -Port 9100 -TimeoutMilliSec 500 } | Select-Object -Property ComputerName, Port, Response