Pages

tirsdag 11. november 2014

PowerShell: Parse netstat for Remote Connections and more...

I needed a way to find out who was connected to my Operations Manager Management Servers.

Of course you can use the GetConnectedUserNames method of the Management Group object in PowerShell like this:
Import-Module OperationsManager
Get-SCOMManagementGroup|%{$_.GetConnectedUserNames()}

But this will only give you the names and not the remote computername. To do this I wrote a PowerShell function that parse netstat output, and also use Invoke-Expression to get the user of a specific process. To use it to list computers/users connected to a specific Operations Manager Management Server, I execute this on the Management Server:
. \RemoteConnections.ps1
Get-RemoteConnection -Port 5724 -ProcessName 'Microsoft.EnterpriseManagement.Monitoring.Console'

You can also list computers/users connected to a specific Service Manager Management Server, like this:
. \RemoteConnections.ps1
Get-RemoteConnection -Port 5724 -ProcessName 'Microsoft.EnterpriseManagement.ServiceManager.UI.Console'

Or list computers/users connected to a specific Virtual Machine Manager Management Server, like this:
. \RemoteConnections.ps1
Get-RemoteConnection -Port 8100 -ProcessName 'VmmAdminUI'

This is how the content of RemoteConnections.ps1 looks like:
function Get-ConnectionData{
  param ( 
    [Parameter(Mandatory=$true)]
    $RemoteAddress
  )

  process {
    try {
      $IPv4Address = ''
      $IPv6Address = ''
      $ComputerName = ''
      $UserName = ''
      if($remoteAddress -match '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}'){
        $IPv4Address = $remoteAddress
        $ComputerName = [System.Net.DNS]::GetHostByAddress($remoteAddress).hostname
      }else{
        $IPv6Address = $remoteAddress
        $tmp = nslookup $remoteAddress 2>$null
        if($tmp[3]){$ComputerName = $tmp[3].split(' ')[4]}
      }

      if($ComputerName -and $ProcessName){
        $UserName = (Invoke-Command -ComputerName $ComputerName -ScriptBlock {param($ProcessName) $owners = @{}; gwmi win32_process -Filter ("Name LIKE '"+$ProcessName+"%'")|% {$owners[$_.handle] = $_.getowner().user};Get-Process -ProcessName $ProcessName -ErrorAction SilentlyContinue|select Id,@{l='Owner';e={$owners[$_.id.tostring()]}}} -ArgumentList $ProcessName).Owner
      }
    }
    catch [System.Exception] {
      Write-Error $_.Exception.Message
    }
    New-Object PSObject -Property @{  
      IPv4Address = $IPv4Address
      IPv6Address = $IPv6Address
      ComputerName = $ComputerName
      Username = $UserName
    }
  }
}

function Get-RemoteConnection{
  <#
  .SYNOPSIS
  Use this function to list Remote Connections
  .DESCRIPTION
  This function will list remote connections
  .EXAMPLE
  Get-RemoteConnection -Port 5724 -ProcessName 'Microsoft.EnterpriseManagement.Monitoring.Console'
  .EXAMPLE
  Get-RemoteConnection -Port 5724 -ProcessName 'Microsoft.EnterpriseManagement.ServiceManager.UI.Console'
  .EXAMPLE
  Get-RemoteConnection -Port 8100 -ProcessName 'VmmAdminUI'
  .PARAMETER Port
  The Port to list connections for
  .PARAMETER Protocol
  The Protocol for the connection. May be any of TCP, TCPv6, UDP or UDPv6.
  If unspecified all protocols are listed
  .PARAMETER ProcessName
  The process name of remote application responsible for the connection.
  If this is specified, we try to get the username that started the process.
  #>
  param (
    [Parameter(Mandatory=$True,
    ValueFromPipeline=$True,
    ValueFromPipelineByPropertyName=$True,
      HelpMessage='What port would you like to list connections for?')]
    [string]$Port,
    [Parameter(Mandatory=$False,
      HelpMessage='What protocol would you like to list connections for?')]
    [string]$Protocol='',
    [Parameter(Mandatory=$False,
      HelpMessage='What remote process on the connected computer do you want owner name for?')]
    [string]$ProcessName=''
    
  )
  if($Protocol -eq ''){
    $lines = netstat -ano
  }else{
    $lines = netstat -ano -p $Protocol
  }
  foreach($line in $lines){
    $cols = $line.Split(' ',[System.StringSplitOptions]::RemoveEmptyEntries)
    if($cols[1] -notmatch '^\[::' -and ($cols[0] -eq 'TCP' -or $cols[0] -eq 'UDP')){
      if(($la = $cols[1] -as [ipaddress]).AddressFamily -eq 'InterNetworkV6'){
        $localPort = $cols[1].split('\]:')[-1]
      }else{
        $localPort = $cols[1].split(':')[-1]
      }
      if(($ra = $cols[2] -as [ipaddress]).AddressFamily -eq 'InterNetworkV6'){
        $remoteAddress = $ra.IPAddressToString
      }else{
        $remoteAddress = $cols[2].split(':')[0]
      }
      if($localPort -like $Port -and $remoteAddress -ne '' -and $remoteAddress -ne '0.0.0.0' -and $remoteAddress -ne '127.0.0.1' -and $remoteAddress -notlike 'fe80*'){
        Get-ConnectionData -RemoteAddress $RemoteAddress
      }
    }
  }
}