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
ManageEngine ADManager Plus - Download Free Trial
Exclusive offer on ADManager Plus for US and UK regions. Claim now!
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
https://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.
Formatting the code in the blog entry is a little wonky. The file you download and run is properly formatted.
You can tell I’m not a developer. I didn’t even think about the .NET classes which will also return this type of information.
$properties=[System.Net.NetworkInformation.IPGlobalProperties]::GetIPGlobalProperties()
$udp=$properties.GetActiveUdpListeners()
$tcp=$properties.GetActiveTcpConnections()
Thanks to JVierra for pointing this out.