Check for open TCP ports using PowerShell

From Svendsen Tech PowerShell Wiki
Jump to: navigation, search

To check a remote computer for open/closed TCP ports using PowerShell version 2 (and up), you can use the Net.Sockets.TcpClient class from the .NET libraries. There's an example below. I also wrote a decent, generic script module for this that you can view examples of how to use - and download - below.

You are free to review the examples below, or to use this script module, but this PowerShell nmap-like script module (PSnmap) that I wrote years later beats this script module hands down in just about every imaginable way (except for how well it's documented). It's orders of magnitude faster, uses less resources, and is overall a more polished product. Subnets can also be specified using CIDR notation in that new script.

I wrote a module called Get-PortState which produces custom PowerShell objects that can be assigned to a variable, sent to Format-Table for console display, exported to CSV, or whatever you might want to do with them. However, for the sake of convenience, there's an -ExportToCsv parameter that allows you to specify a CSV output file name directly when you run the Get-PortState command. You also have the data/objects from the last Get-PortState run available with the command Get-PortStateLast and can pass that to Export-Csv (or ConvertTo-Csv and Set-Content).

One thing I noticed in my lab environment, is that the timeout for closed and filtered ports against some computers seems to be pretty long. Due to this, I added an -AsJob parameter and a -Timeout parameter (default of 3000 ms / 3 seconds if omitted; only in use together with -AsJob). This will speed up processing in many scenarios, but at the expense of using more resources as it spawns a powershell.exe process for each port connection attempt.

Just as I was about to release the module, I thought it'd be nice to have a DNS lookup feature, and tried to implement this sanely and flexibly. If you pass in an IP that resolves to a host name, you will get the host name; if you pass in a host name that resolves to one or more IP addresses, you will get the IP address(es) - joined together with semicolons if there are more than one.

If you can't install modules (even for your user only), I made sure you can dot-source the script, as demonstrated in the below screenshot example.

The old script I initially put up is still available at the bottom of this article, in case someone wants it, but I do recommend getting the new one. The old one only works against a single target host and is mostly a telnet replacement for checking for open ports a bit more gracefully, as I mention there. The newer script does that, and more, and is more consistent with recommended PowerShell cmdlet design (but not 100%).

Let's start with a simple code example in case you want to roll your own:

foreach ($Computer in $ComputerName) {
    foreach ($Port in $Ports) {
        # Create a Net.Sockets.TcpClient object to use for
        # checking for open TCP ports.
        $Socket = New-Object Net.Sockets.TcpClient
        # Suppress error messages
        $ErrorActionPreference = 'SilentlyContinue'
        # Try to connect
        $Socket.Connect($Computer, $Port)
        # Make error messages visible again
        $ErrorActionPreference = 'Continue'
        # Determine if we are connected.
        if ($Socket.Connected) {
            "${Computer}: Port $Port is open"
        else {
            "${Computer}: Port $Port is closed or filtered"
        # Apparently resetting the variable between iterations is necessary.
        $Socket = $null

To just check for an open port on a single host, you can use something like the code below. This also has the added bonus of using an asynchronous call with a specified timeout in milliseconds ("3000" here).

#$computer, $port = $args[0,1] # assign values to these
$mysock = new-object net.sockets.tcpclient
$IAsyncResult = [IAsyncResult] $mysock.BeginConnect($computer, $port, $null, $null)
measure-command { $succ = $iasyncresult.AsyncWaitHandle.WaitOne(3000, $true) } | % totalseconds

To emulate "netstat" and show locally listening ports, established connections, and remote ports that one single, local server is connected to, you can use this code:

$NetworkProperties = [System.Net.NetworkInformation.IPGlobalProperties]::GetIPGlobalProperties()

$TcpConnections = $NetworkProperties.GetActiveTcpConnections()

$TcpConnections | Format-Table -AutoSize

      State LocalEndPoint                      RemoteEndPoint                   
      ----- -------------                      --------------                   

$TcpConnections | Select -Exp LocalEndpoint -First 3 | ft -AutoSize

AddressFamily Address    Port
------------- -------    ----
 InterNetwork 49156
 InterNetwork 49157
 InterNetwork 49158


The module Get-PortState exports two commands:

  • Get-PortState
  • Get-PortStateLast


  • - Download, remember to unblock, and unzip to a PowerShell modules folder. See the variable $env:PSModulePath. Or rename the .psm1 file to .ps1 and dot-source it. You can also run Import-Module against a (relative) arbitrary path containing the module.

But you probably do want PSnmap instead. It's so much better, and so much faster. Just have a look at that screenshot example.

Screenshot Example


NB! If you use the variable name $Data and dot-source the module/script rather than using it as a module, Get-PortStateLast will fail as the variable names collide. I'm using $Data in the script (I probably should change the variable name). The variable $Properties is also used.

However, if you use it as a module and import it with the Import-Module command, the variables will be correctly scoped to the module, and you won't have this issue.

Get-PortState Parameters

-ComputerName Required. Target computer name(s) or IP address(es).
-Port Required. Comma-separated list of ports to check whether are open.
-ExportToCsv Optional. Create a CSV report and save it to the specified file name, using UTF-8 encoding. The file will be overwritten without you being prompted, if it exists.
-Timeout Optional. Timeout in milliseconds before the script considers a port closed. Default 3000 ms (3 seconds). For speeding things up. Only in effect when used with the -AsJob parameter.
-AsJob Optional. Use one job for each port connection attempt. This allows you to override the possibly lengthy timeout from the connecting socket, and a port is considered closed if we haven't been able to connect within the allowed time. Default 3000 ms. See the -Timeout parameter. This may be quite resource-consuming! Ports that are determined to be closed via timeout will be tagged with a "(t)" for timeout.
-Dns Optional. Try to determine IP if given a host name or host name if given an IP. Multiple values are joined with semicolons: ";".
-NoPing Optional. Do not try to ping the target computer if this is specified. By default, the script skips the port checks on targets that do not respond to ICMP ping and populate the fields with hyphens for these hosts. Be aware that computers that do not resolve via DNS/WINS/NetBIOS will also be reported as having failed the ping check.
-ContinueOnPingFail Optional. Try to check the target computer for open ports even if it does not respond to ping. Be aware that computers that do not resolve via DNS/WINS/NetBIOS will also be processed like this (and it should report them as "closed").
-Quiet Optional. Do not display results with Write-Host directly as the script progresses.
-NoSummary Optional. Do not display a summary at the end. The summary includes start and end time of the script, and the output file name, if -ExportToCsv was specified.

A Few Simple Get-PortState Examples

Here is a simple example that just gets two open ports from one target, without using jobs. When ports are open, it usually goes fast. As you can see in the summary, it finishes in less than a second. However, if you do not use the -AsJob parameter, you can experience "hangs" of 20-30 seconds when ports are closed or filtered on some target hosts.

PS C:\PowerShell> Import-Module Get-PortState
PS C:\PowerShell> Get-PortState -comp 2008r2esxi -Port 3389,80 -Dns -Quiet

Start time:  07/29/2012 10:51:14
End time:    07/29/2012 10:51:14

ComputerName : 2008r2esxi
Ping         : Yes
IP/DNS       :
Port 80      : Open
Port 3389    : Open

PS C:\PowerShell> Get-PortStateLast | ft -a

ComputerName Ping IP/DNS        Port 80 Port 3389
------------ ---- ------        ------- ---------
2008r2esxi   Yes Open    Open

To also suppress the summary, use the -NoSummary parameter along with -Quiet. Here I also pipe to Format-Table -Autosize (ft -a) for a neater display of the information.

PS C:\PowerShell> Import-Module Get-PortState
PS C:\PowerShell> Get-PortState -comp 2008r2esxi -Port 3389,80 -Dns -Quiet -NoSummary | ft -a

ComputerName Ping IP/DNS        Port 80 Port 3389
------------ ---- ------        ------- ---------
2008r2esxi   Yes Open    Open

Using Get-PortStateLast

So you might find yourself forgetting to assign the results to a variable, and also forgetting to use the -ExportToCsv parameter. With this in mind, I implemented a Get-PortStateLast command that'll give you the custom PowerShell objects from the last Get-PortState command run (unless you've dot-sourced the script and have later changed the $Data variable).

PS C:\PowerShell> Import-Module Get-PortState
PS C:\PowerShell> Get-PortState -Comp,vista64esxi,ubuntu64esxi,
-Port 80,22,3389,445,20000 -AsJob -Dns -quiet | ft -a

Start time:  07/29/2012 11:16:08
End time:    07/29/2012 11:17:03

ComputerName  Ping IP/DNS                    Port 22    Port 80    Port 445   Port 3389  Port 20000
------------  ---- ------                    -------    -------    --------   ---------  ---------- Yes  2008r2esxi.svendsen.local Closed (t) Open       Open       Open       Closed (t) Yes             Open       Closed (t) Closed (t) Closed (t) Closed (t)
ubuntu64esxi  Yes             Open       Open       Open       Closed     Closed
vista64esxi   Yes             Closed (t) Closed (t) Open       Open       Closed (t)

PS C:\PowerShell> # I wanted a CSV file and don't want to start over. Now what?
PS C:\PowerShell> Get-PortStateLast | Export-Csv -NoType -Enc utf8 port-report.csv
PS C:\PowerShell> Import-Csv .\port-report.csv | ft -a

ComputerName  Ping IP/DNS                    Port 22    Port 80    Port 445   Port 3389  Port 20000
------------  ---- ------                    -------    -------    --------   ---------  ---------- Yes  2008r2esxi.svendsen.local Closed (t) Open       Open       Open       Closed (t) Yes             Open       Closed (t) Closed (t) Closed (t) Closed (t)
ubuntu64esxi  Yes             Open       Open       Open       Closed     Closed
vista64esxi   Yes             Closed (t) Closed (t) Open       Open       Closed (t)
PS C:\PowerShell> # voila

Another Get-PortState Example

Here I get the IPv6 and IPv4 addresses for a Windows server connected to from a Windows 7 client, as well as whether the default RDP port is available:

PS C:\> Import-Module Get-PortState
PS C:\> Get-PortState -comp 2008r2esxi -port 3389 -Dns -quiet -nosum | ft -a

ComputerName Ping IP/DNS                                     Port 3389
------------ ---- ------                                     ---------
2008r2esxi   Yes  fe80::b434:bc55:a730:d672%10; Open

PS C:\> Get-PortStateLast | select -expand 'IP/DNS' | %{ $_ -split ';' }
PS C:\>

Using Get-PortState From Another Script

I don't know how likely it is, but in case someone wants to use the Get-PortState command from another script, or for other purposes than making a report, I should briefly address how you would use the script/module in this regard.

Something like this is the least you can get away with:

PS C:\> if ( (Get-PortState ubuntu64esxi 22 -nos -q).'Port 22' -eq 'Open' ) { 'Do stuff' } else { 'Do other stuff' }
Do stuff

PS C:\> if ( (Get-PortState ubuntu64esxi 23 -nos -q).'Port 23' -eq 'Open' ) { 'Do stuff' } else { 'Do other stuff' }
Do other stuff

Old Check-Open-Ports.ps1 Script

Initially I wrote a simpler version of the script that's now featured on this page. I'm keeping it along with the original description for the time being:

This PowerShell script checks for open ports, or an open port, using the .NET class Net.Sockets.TcpClient. Why didn't I think of this sooner? Essentially it's a replacement for using telnet for checking for open ports. I use this script now, rather than having to start a separate cmd window for each port to test with telnet, and then having the useless cursor in the top left corner, and usually having to close the window or wait for a timeout, if the port is open.

It works like this:


Download Old Check-Open-Ports.ps1

Download Check-Open-Ports.ps1.txt here (right-click and "save as").