0

So I wanted to get a powershell command to list the firewall rules and ports, and I had to basically use 2 commands: Get-NetFirewallRule and Get-NetFirewallPortFilter.

So I basically came up with this:

Get-NetFirewallPortFilter | where-object {$_.LocalPort -cmatch '[0-9]+'}|select-object -Property @{n='Name';e={($_ | Get-NetFirewallRule).Name}}, @{n='Description';e={($_ | Get-NetFirewallRule).description}}, Protocol, LocalPort, RemotePort, @{n='Direction';e={($_|Get-NetFirewallRule).Direction}}

I went with Get-NetFirewallPortFilter first because I wanted to only get numerical ports (so I didn't want to get 'Any' as a result).

Problem is, the more calculated properties I have, the slower this runs. It generates like 3 lines a second, which IMO is very slow.

I timed the command to see how long it takes (did an Out-GridView to also see the output, I don't think it adds THAT much more to the time, it seems to generate just as many lines a second):

PS C:\> measure-command { Get-NetFirewallPortFilter | where-object {$_.LocalPort -cmatch '[0-9]+'}|select-object -Property @{n='Name';e={($_ | Get-NetFirewallRule).Name}}, @{n='Description';e={($_ | Get-NetFirewallRule).description}}, Protocol, LocalPort, RemotePort, @{n='Direction';e={($_|Get-NetFirewallRule).Direction}} |out-gridview }


Days              : 0
Hours             : 0
Minutes           : 1
Seconds           : 59
Milliseconds      : 143
Ticks             : 1191434968
TotalDays         : 0.00137897565740741
TotalHours        : 0.0330954157777778
TotalMinutes      : 1.98572494666667
TotalSeconds      : 119.1434968
TotalMilliseconds : 119143.4968

It output 358 lines in ~119 seconds.

Are there any optimizations I can run to make this run faster?

  • Why trying to press everything in a single line? `Get-NetFirewallRule` is apparently an expensive cmdlet. So, save the result in a variable (`$FirewallRule = $_ |Get-NetFirewallRule`) and (re)use the variable in your calculated expressions. – iRon Jul 19 '21 at 10:48
  • @iRon but how do I do that here? Doesn't it break the pipeline if I declare a variable and then just move on like nothing happened? What I mean is, I don't know how to structure this on the command line, where do I declare the variable? And where do I use the command separator `;` ? – Lhakryma DL Jul 19 '21 at 11:27

2 Answers2

0

You are using Get-NetFirewallRule multiple times in your code, where you could do this only once per PortFilter:

Get-NetFirewallPortFilter | Where-Object {$_.LocalPort -match '[0-9]+'} | ForEach-Object {
    $rule = $_ | Get-NetFirewallRule
    $_ | Select-Object @{Name = 'Name'; Expression = {$rule.Name}},
                       @{Name = 'Description'; Expression = {$rule.Description}},
                       Protocol, LocalPort, RemotePort, 
                       @{Name = 'Direction'; Expression = {$rule.Direction}}
} | Out-GridView

It might be just a tad faster if you take out the ForEach-Object and use a foreach() instead:

$portFilters = Get-NetFirewallPortFilter | Where-Object {$_.LocalPort -match '[0-9]+'}
$result = foreach ($filter in $portFilters) {
    $rule = $filter | Get-NetFirewallRule
    $filter | Select-Object @{Name = 'Name'; Expression = {$rule.Name}},
                            @{Name = 'Description'; Expression = {$rule.Description}},
                            Protocol, LocalPort, RemotePort, 
                            @{Name = 'Direction'; Expression = {$rule.Direction}}
}
$result | Out-GridView
Theo
  • 57,719
  • 8
  • 24
  • 41
  • Thanks, this lowered the time down to 39 seconds, but this still isn't what I would call "fast", and it definitely isn't as fast as it would run on linux – Lhakryma DL Jul 19 '21 at 11:32
  • @Lhakryma, you using Powershell on Linux? – Abraham Zinala Jul 19 '21 at 13:13
  • Oh no, I mean doing the relatively same thing, but on linux, would run much faster. – Lhakryma DL Jul 19 '21 at 13:23
  • Then go use Linux? Each tools have strengths and weaknesses. If you came to powershell for speed I think you’ve been misled. – Doug Maurer Jul 19 '21 at 14:49
  • @DougMaure I came to powershell not knowing what to expect. I was just thinking about efficiency here, don't shoot me lol – Lhakryma DL Jul 19 '21 at 16:14
  • Can you show your linux equivalent and the time it took? I'm genuinely curious. – Doug Maurer Jul 19 '21 at 16:17
  • @DougMaurer just your basic `iptables -L` and `awk` with an `if` from there, but I now realize it's not really a fair comparison since on linux the firewall doesn't allow to set per application rules. – Lhakryma DL Jul 22 '21 at 11:39
  • @LhakrymaDL Well, there you go then.. My answer got the processing time down from (almost) 2 minutes to (almost) 40 seconds. Sure, still not blistering fast, but I don't think it can be done any faster on Windows. – Theo Jul 22 '21 at 11:45
  • @LhakrymaDL Please see my edit. Changing `ForEach-Object` to `foreach()` could save you an extra second or two.. – Theo Jul 22 '21 at 11:52
  • @Theo I thought `foreach()` was an alias for `ForEach-Object`, how are they different? – Lhakryma DL Jul 26 '21 at 09:49
  • @LhakrymaDL No, I agree it's confusing, but `ForEach-Object {...}` is a cmdlet, while `foreach($thing in $collection)` is a statement (or keyword if you like). Generally, using `foreach(...)` works faster because the cmdlet has more overhead. There are lots of discussions about the differences, for instance [this one](https://stackoverflow.com/questions/29148462/difference-between-foreach-and-foreach-object-in-powershell) and [Getting to Know ForEach and ForEach-Object](https://devblogs.microsoft.com/scripting/getting-to-know-foreach-and-foreach-object/) – Theo Jul 26 '21 at 11:17
0

Filtering left is always fastest, with get-netfirewallportfilter first. This takes about 18 seconds. I had trouble using -pipelinevariable (or -pv) with get-netfirewallportfilter. Powershell commands manipulate objects vs linux commands that manipulate text.

EDIT: Piping to get-netfirewallrule behaves oddly, like all the objects would be processed in one shot. I put it inside a foreach-object. I changed name to displayname and put description last.

# not work
Get-NetFirewallPortFilter -pipelinevariable port
Get-NetFirewallPortFilter : Cannot retrieve the dynamic parameters for the cmdlet. Object reference not set to an
instance of an object.
At line:1 char:1
+ Get-NetFirewallPortFilter -pipelinevariable port
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [Get-NetFirewallPortFilter], ParameterBindingException
    + FullyQualifiedErrorId : GetDynamicParametersException,Get-NetFirewallPortFilter
Get-NetFirewallPortFilter |
where localPort -match \d -pipelinevariable port |
% { $_ | Get-NetFirewallRule } | select displayname,
@{n='Protocol';  e={$port.protocol}},
@{n='Localport'; e={$port.localport}},
@{n='Remoteport';e={$port.remoteport}},
direction, description | ft
DisplayName                    Protocol Localport Remoteport Direction description
-----------                    -------- --------- ---------- --------- -----------
SNMP Trap Service (UDP In)     UDP      162       Any          Inbound Inbound rule for the SNMP Trap Service to allow SNMP traps. [UDP 162]
SNMP Trap Service (UDP In)     UDP      162       Any          Inbound Inbound rule for the SNMP Trap Service to allow SNMP traps. [UDP 162]
Delivery Optimization (TCP-In) TCP      7680      Any          Inbound Inbound rule to allow Delivery Optimization to connect to remote endpoints
js2010
  • 23,033
  • 6
  • 64
  • 66