Check whether a port is open on a bulk list of endpoints – and FAST!

Hooray and welcome to 2023! May all of your dreams come true this year, and if not, may we at least be sheltered from P1 outages and other disasters, either natural or man-made…!

🤞🙏🤞🙏🤞🙏🤞🙏🤞🙏🤞🙏🤞🙏🤞🙏🤞🙏

Recently I needed to see if RDP was open on hundreds of machines. No trouble I thought, and wrote a wee script that looped through a list of names or IPs using Test-NetConnection with the -Port switch.

This worked no problem, BUT… Test-NetConnection can take 15+ seconds per machine (especially when waiting to ‘time out’ if the port is closed). Not good enough, we want it to be faster!

Unfortunately, Test-NetConnection does not currently support any switches to control how long it waits for a response, although I’m sure they will add it at some point.

So, a Googling we go then! And again, I am reminded of my average intelligence when it comes to PowerShell. But that’s okay – learning all the time is a good thing, right? 😜

I found this little beowtae of a function called Test-PortScan which uses the TcpClient object to do the job faster. The original is found here. Thanks to the author ‘BSonPosh’. 👌

As I created this script I also thought “I’d like any functions to be at the bottom of the script, so the main editable code is at the top”. Turns out this is quite easy to do by storing the main code in a variable, then calling it after the functions. Full script below; you’ll want to edit the variables at the top – Name of the service or test, the port number, the timeout value (I went with half a second) and change the import and logfile locations if required. Cheers. Simon! 🤟🙂🤟

# uses test-portscan function to quickly check whether a port is open on multiple IP4 endpoints

$mainblock = {
Clear-Host

# set variables
$logfile = 'c:\temp\portcheck_log.txt'
$servicename = 'HTTP'
$portnumber = '80'
$timeoutmilliseconds = '500'

# import single column of endpoints from a file (no header required)
$endpoints = Get-Content C:\Temp\endpoints.txt -Force

# or use an array
# $endpoints = @(
# "192.168.1.254"
# "1.1.1.1"
# "8.8.8.8"
# "www.microsoft.com"
# "www.bleepingcomputer.com"
# )

# create log file
$null = New-Item -ItemType File -Path $logfile -Force

# perform the checks
foreach ( $endpoint in $endpoints ) {

    Write-Host "Testing $testname access to $endpoint..."

    if ((Test-PortScan -Devices $endpoint -StartPort $portnumber -EndPort $portnumber -Timeout $timeoutmilliseconds).Open -ne 'False') {
       Write-Host -ForegroundColor Gray "$testname is not open on $endpoint."
    }
        else {
            Write-Host -ForegroundColor Green "$testname is open on $endpoint! - adding to $logfile"
            $output = "$testname is open on $endpoint."
            $output | Out-File $logfile -Append -Force
        }
}

# end of mainblock
}

# port checking function
function Test-PortScan
{   
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true,
                   ValueFromPipeline = $true,
                   ValueFromPipelineByPropertyName = $true,
                   Position = 0,
                   HelpMessage = 'You must supply at least a device name or IP')]
        [Alias('Name')]
        [string]$Devices,
        [Parameter(Position = 1)]
        [int]$StartPort = 1,
        [Parameter(Position = 2)]
        [int]$EndPort = $StartPort,
        [Parameter(Position = 3)]
        [int]$Timeout = 100
    )

    BEGIN
    {
        $PSObject = @()
        $Output = @()
    }
    PROCESS
    {
        foreach ($Device in $Devices)
        {
            Write-Verbose "Not to the port loop yet"
            $Port = $StartPort
            do
            {
                #($Port = $StartPort; $Port -gt $EndPort; $Port++)              
                Write-Verbose "Made it to the port loop"
                $requestCallback = $state = $null
                $client = New-Object System.Net.Sockets.TcpClient
                $beginConnect = $client.BeginConnect($Device, $Port, $requestCallback, $state)
                Start-Sleep -Milliseconds $Timeout
                if ($client.Connected) { $Open = $true }
                else { $Open = $false }
                $client.Close()
                [pscustomobject]@{
                    'Computer' = $Device
                    'Port'     = $Port
                    'Open'     = $Open
                }
                $Port += 1
            }
            until ($Port -gt $EndPort)
        }
        
    }
    END
    {
        
    }
}

# run mainblock
& $mainblock

# end

Loading

1 thought on “Check whether a port is open on a bulk list of endpoints – and FAST!”

Leave a Reply to Sailesh Singh Cancel Reply

Your email address will not be published. Required fields are marked *

Scroll to Top