Why is firewall rule enumeration slow in PowerShell?

72 Views Asked by At

I'd like to enumerate my Windows firewall rules, but the run through takes a while...

Measure-Command {

  $FirewallRules = Get-NetFirewallRule |
    where {
      $_.Enabled -eq $true -and
      $_.Direction -eq 'Inbound'
    } | Get-NetFirewallPortFilter

}

TotalSeconds      : 6,3937642

Without getting the actual port filters (Get-NetFirewallPortFilter), the enumeration is only

TotalSeconds      : 0,3220308

Is there a faster way to accomplish this?

Edit: I would prefer if any alternate code works with plain user accounts.

2

There are 2 best solutions below

8
Mathias R. Jessen On

The slowdown is caused by the fact that Get-NetFirewallPortFilter performs a relatively expensive ASSOCIATORS OF WMI query for each firewall rule you pipe to it.

For any more than a handful of rules you can get a significant performance improvement by simply fetching all port filters up front and then doing the correlation yourself:

# index all port filters
$portFilterIndex = @{}
Get-NetFirewallPortFilter -All |ForEach-Object {
  $portFilterIndex[$_.InstanceID] = $_
}

# get relevant rules, grab port filter from index
Get-NetFirewallRule -Direction Inbound |Where-Object Enabled |Select-Object *,@{Name='PortFilter'; Expression = {$portFilterIndex[$_.ID]}}
0
Dennis On

I found this at TechNet, archived at Wayback Machine.
TechNet, James O'Neill's blog - How to manage the Windows firewall settings with PowerShell

I fixed up the formatting for better readability...

The solution involves the ComObject HNetCfg.FwPolicy2

$FwProfileTypes = @{
  1GB = 'All'; 1 = 'Domain'; 2 = 'Private' ; 4 = 'Public'
}

$FwAction = @{
  1 = 'Allow'; 0 = 'Block'
}

$FwProtocols = @{
  1  = 'ICMPv4' ;2 = 'IGMP'       ;6 = 'TCP'        ;17 = 'UDP'  ;    
  41 = 'Pv6'    ;43 = 'IPv6Route' ;44 = 'IPv6Frag'  ;47 = 'GRE'  ;
  58 = 'ICMPv6' ;59 = 'IPv6NoNxt' ;60 = 'IPv6Opts'  ;112 = 'VRRP';
  113 = 'PGM'   ;115 = 'L2TP';

  'ICMPv4' = 1  ;'IGMP'      = 2  ;'TCP'      = 6   ;'UDP' = 17  ; 
  'IPv6'   = 41 ;'IPv6Route' = 43 ;'IPv6Frag' = 44  ;'GRE' = 47  ; 
  'ICMPv6' = 48 ;'IPv6NoNxt' = 59 ;'IPv6Opts' = 60  ;'VRRP' = 112;
  'PGM'    = 113;'L2TP'      = 115
}

$FwDirection = @{
  1 = 'Inbound' ;2 = 'Outbound';
  'Inbound' = 1 ;'Outbound' = 2
} 
 
function Convert-FWProfileType {
  Param (
    $ProfileCode
  )

  $FwProfileTypes.Keys |
    foreach `
    –begin {
      [String[]]$Descriptions = @()
    } `
    -process {
      if ($ProfileCode -bAND $_) {
        $Descriptions += $FWProfileTypes[$_]
      } 
    } `
    –end {$Descriptions}
}

function Get-FirewallConfig {
  $Fw = New-Object –ComObject HNetCfg.FwPolicy2

  'Active Profiles(s) :' + (Convert-FWProfileType $Fw.CurrentProfileTypes)

  @(1,2,4) |
    select @{
      l = 'Network Type'      ;e = { $FwProfileTypes[$_] }
    },
    @{
      l = 'Firewall Enabled'  ;e = { $Fw.FireWallEnabled($_) }
    },
    @{
      l = 'Block All Inbound' ;e = { $Fw.BlockAllInboundTraffic($_) }
    },
    @{ 
      l = 'Default In'        ;e = { $FwAction[$fw.DefaultInboundAction($_)] }
    },
    @{ 
      l = 'Default Out'       ;e ={ $FwAction[$Fw.DefaultOutboundAction($_)] }
    } |
      Format-Table -Auto
}

function Get-FireWallRule {
  Param (
    $Name,
    $Direction,
    $Enabled,
    $Protocol,
    $Profile,
    $Action,
    $Grouping
  )

  $Rules = (New-Object –ComObject HNetCfg.FwPolicy2).Rules

  if ($Name)      {$Rules = $Rules | where {$_.Name      –like $Name}}
  if ($Direction) {$Rules = $Rules | where {$_.Direction -eq   $Direction}}
  if ($Enabled)   {$Rules = $Rules | where {$_.Enabled   -eq   $Enabled}}
  if ($Protocol)  {$Rules = $Rules | where {$_.Protocol  -eq   $Protocol}}
  if ($Profile)   {$Rules = $Rules | where {$_.Profiles  -bAND $Profile}}
  if ($Action)    {$Rules = $Rules | where {$_.Action    -eq   $Action}}
  if ($Grouping)  {$Rules = $Rules | where {$_.Grouping  -like $Grouping}}

  $Rules
}

Get-FirewallRule -Enabled $true | 
  sort Direction, ApplicationName, Name | 
    format-table -Wrap -Autosize -Property Name, 
      @{ 
        l = 'Action'    ;e = { $FwAction[$_.Action] }
      }, 
      @{ 
        l = 'Direction' ;e = { $FwDirection[$_.Direction] }
      },
      @{ 
        l = 'Protocol'  ;e = { $FwProtocols[$_.Protocol] }
      },
      LocalPorts,
      ApplicationName 

It completes the run as a plain user in

TotalSeconds : 0,2342957