Get Your TCP Ports Here!

Once again, the PowerShell forum at ScriptingAnswers.com has undone me. I was answering a question about running Netstat in a PowerShell session on a remote computer which got me thinking about a PowerShell function to turn NETSTAT.EXE output into objects. Once you have an object then you can do all sorts of things.  Needless to say I got hooked on working something up instead of working on what I had planned for the day. The good news is that you get a tool and there’s still a few hours of the day left for me to get something else accomplished. I’m sure there are plenty of variations on this topic already out there, but here’s my contribution: Get-TCP.
[cc lang=”Powershell”]
Function Get-TCP {
<# .Synopsis Get TCP Netstat information .Description This function calls the command line NETSTAT.EXE tool and returns an object representation of the TCP data. Protocol   : TCP Localhost  : 172.16.10.127 LocalPort  : 49259 RemoteHost : 74.201.86.29 RemotePort : https Status     : ESTABLISHED The default is the local computer without name resolution. However you can specify a remote computername, assuming the remote computer is running PowerShell 2.0 and has remoting enabled. Use -ResolveHost to resolve IP addresses to host names. This is a little slower. This function will only return IPv4 hosts and addresses .Parameter Computername The name of the computer to query. The default is the localhost. .Parameter ResolveHost Resolve IP addresses to host names. This is a little slower. The is the equivalent of running Netstat.exe without any parameters. .Parameter ResolvePort Resolve the service name associated with the port. This requires access to the legacy Services file found at $env:windir\system32\drivers\etc\services. .Parameter IncludeRaw Include the raw netstat data. .Example PS C:\> get-tcp Return TCP Netstat information for the local computer. .Example PS C:\> get-tcp “Server1″,”Server2” -resolve

Return TCP Netstat information for computers Server1 and Server2 with resolved IP addresses.
.Example
PS C:\> get-content computers.txt } get-tcp -resolveHost | where {$_.RemotePort -eq 80} | format-table -autosize

Get HTTP connections for every computer in computers.txt and present as a formatted table.
.Example
PS C:\> get-tcp | sort RemotePort | Select RemotePort -unique

Get a sorted list of all remote connections by port.
.Inputs
Strings
.Outputs
Custom object
.Link
http://jdhitsolutions.com/blog

.Link
Invoke-Command

.Notes
NAME:      Get-TCP
VERSION:   1.5
AUTHOR:    Jeffery Hicks
LASTEDIT:  July 29, 2010

Learn more with a copy of Windows PowerShell 2.0: TFM (SAPIEN Press 2010)

#>

[cmdletbinding()]

Param(
[Parameter(Position=0,ValueFromPipeline=$True)]
[string[]]$Computername=$env:computername,
[switch]$ResolveHost,
[switch]$ResolvePort,
[switch]$IncludeRaw
)

Begin {
Write-Verbose “Starting $($myinvocation.mycommand)”
if ($ResolveHost) { Write-Verbose “Resolving host names”}
if ($ResolvePort) {Write-Verbose “Resolving port names”}
#put everything into a script block so that if the computer
#is remote it can be executed using Invoke-Command
$scriptblock={netstat.exe -n | where {$_.Contains(“TCP”) -AND $_ -notmatch “\[“}}

Write-Verbose “Caching Services information”
$file=”$env:windir\system32\drivers\etc\services”
#get just tcp services
$services=Get-Content -Path $file | Select-String -Pattern “/tcp”

Write-Verbose $($scriptblock.ToString())

#define the name resolution function
Function Resolve-DNS  {

[cmdletbinding()]

Param(
[Parameter(Position=0,Mandatory=$True,ValueFromPipeline=$True,
HelpMessage=”Enter an IP Address to resolve”)]
[string[]]$Address
)
Begin {
Write-Verbose “Starting Resolve-DNS”
#turn off error pipeline
$errorActionPreference=”SilentlyContinue”
}
Process {
foreach ($IP in $Address) {
Write-Verbose “Resolving $IP”
$dns=[System.net.DNS]::GetHostByAddress(“$IP”)
if (-not $dns) {
Write-Verbose “no record found for $IP”
$h=$IP
$a=$null
}
else {
$h=$dns.hostname
$a=$dns.AddressList
}
New-Object -TypeName PSobject -Property @{
Hostname=$h
Addresses=$a
}
} #foreach
} #process

End {
#turn On error pipeline
$errorActionPreference=”Continue”
}
} #end function

} #begin

Process {
foreach ($computer in $computername) {
Write-Verbose “Getting raw NETSTAT data from $($computer.toUpper())”
if ($computer -eq $env:computername) {
#just run the script block if local computer
$data=&$scriptblock
}
else {
Try {
$data=Invoke-Command -ScriptBlock $scriptblock -computername $computer -errorAction “Stop”
}
Catch {
Write-Warning “Failed to run command on $computer”
Write-Warning $error[0].exception.message
}
} #else
#process data
Write-Verbose “Returned $($data.count) items”
Write-Verbose “Parsing data”
#initialize a hash table for IP names
$hash=@{}
foreach ($tcp in $data) {
Write-Verbose $tcp.trim()
#split the line removing empty spaces
$arr=$tcp.trim().split() | where {$_}
$localData=$arr[1].Split(“:”)
$remoteData=$arr[2].Split(“:”)
$protocol=$arr[0]
$raw=$tcp.trim()
$local=$localData[0]
[int]$localPort=$localData[1]
$remote=$remoteData[0]
[int]$remotePort=$remoteData[1]
$status=$arr[3]

#if resolve host requested build a dictionary or
#ip addresses so we don’t need to resolve one already resolved
#and change the property value accordingly
if ($resolveHost) {
if ($hash.contains(“$local”)) {
$local=$hash.Item(“$local”)
}
else {
#look up name
Write-Verbose “Resolving $local”
$localLookup=Resolve-DNS “$local”
#add to hash
$hash.Add(“$local”,$localLookup.Hostname)
#update property
$local=$localLookup.Hostname
}
#do the same for remote
if ($hash.contains(“$remote”)) {
$remote=$hash.Item(“$remote”)
}
else {
#look up name
Write-Verbose “Resolving $remote”
$remoteLookup=Resolve-DNS “$remote”
#add to hash
$hash.Add(“$remote”,$localLookup.Hostname)
#update property
$remote=$RemoteLookup.Hostname
}
} #if $resolveHost

#resolve ports if specified
if ($resolvePort) {
#this is a mini scriptblock to return the service name
#in lieu of a full-blown function
$getsvc={Param ([string]$port)
Write-Verbose “searching for $port”
$service=$services | Select-String -pattern  “\s$port/tcp”

if ($service) {
$data=$service.ToString().Trim().split() | where {$_}
Write-Output $data[0]
}
else {
Write-Output $port
}

} #end $getSvc

[string]$localPort=&$getsvc $localPort
[string]$remotePort=&$getsvc $remotePort

} #end resolveport

#create a new object and pipe it to Select-Object so that
#the properties are in a nice order
$obj=New-Object -TypeName “PSObject” -Property @{
Protocol=$protocol
Localhost=$local
LocalPort=$localPort
RemoteHost=$remote
RemotePort=$remotePort
Status=$status
}
if ($IncludeRaw) {
$obj | Add-Member -MemberType “Noteproperty” -Name “Raw” -Value $raw -PassThru |
Select-Object -Property Protocol,LocalHost,LocalPort,RemoteHost,RemotePort,Status,Raw
}
else {
$obj | Select-Object -Property Protocol,LocalHost,LocalPort,RemoteHost,RemotePort,Status
}
} #foreach $tcp
} #foreach computer
} #Process

End {
Write-Verbose “Ending $($myinvocation.mycommand)”
}

} #end function[/cc]
This is an advanced Windows PowerShell 2.0 function. By default it converts output from Netstat -n to object. I’ve parsed the output so you get an object like this for each line.

[cc lang=DOS]
Protocol   : TCP
Localhost  : 172.16.10.122
LocalPort  : 60553
RemoteHost : 74.201.86.29
RemotePort : 443
Status     : ESTABLISHED[/cc]

When you run Netstat without any parameters it resolves both host and port names. I’ve separated the two so you can use either -ResolveHost and/or -ResolvePort. The former uses an embedded function to get the host entry name by IP address. If not found, then the IP address is used. This function by the way only returns IPv4 information. Service ports are resolved by finding the associated TCP port in the legacy Services file. What you end up with is something like this:

The function can also accept a remote computername as it uses Invoke-Command to run the Netstat command remotely.  All the results are then processed locally. You can pipe computernames to the function or it accepts arrays.

If you have any questions on the nitty-gritty details, please post a comment.

Download Get-TCP.ps1.

Remote PowerShell Performance Comparison

Fellow Windows PowerShell MVP Marco Shaw clued me in on a Microsoft blog post that did a terrific job of comparing, from a performance perspective the different PowerShell 2.0 techniques you can use when managing remote computers. The results are pretty much as I would expect.

Continue reading

The PowerShell Balloon Festival

I trust by now you are realizing how valuable Windows PowerShell  is as a management tool. With a one line command you can accomplish an extraordinary amount of work. Sometimes this work may be long running, which is where background jobs come in handy. Or you may simply kick off a long running script and go about your other administrative tasks. Unfortunately, you have to keep stopping what you’re doing to check and see if your script or job has finished. But there is a better way, assuming you are running Windows Vista or later.

Continue reading

PModem and Folder Listings

PowerShell MVP Oisin Grehan posted a very promising PowerShell module the other day. He calls it the PModem File Transfer Protocol. It is based on the old bulletin board file transfer protocols of the late 20th century, which I have to admit I fondly remember using. Of course Oisin’s work intrigued me and after playing with it for a while I realized I needed something else.

Continue reading