How can I speed up powershell to get firewall rules on windows 10?

瘦欲@ 提交于 2019-12-25 20:01:15

问题


I need to enumerate all firewall rules on windows 10 using PowerShell. I switched to PowerShell from netsh because some built-in rules were getting funny names like @{microsoft.windows.shellexperiencehost_10.0.17134.1_neutral_neutral_cw5n1h2txyewy?ms-resource://microsoft.windows.shellexperiencehost/resources/pkgdisplayname} which I was unable to manage with netsh. Switching to PowerShell shows that the real name was a UID and fixed my problem but the code is really slow to run:

PS C:\Users\vagrant\Desktop> Measure-Command {.\ps-slow.ps1 show}
Seconds           : 48
...

In contrast to Measure-Command {show-netfirewallrule}:

...
Milliseconds      : 644

And netsh:

PS C:\Users\vagrant\Desktop> Measure-Command { netsh advfirewall firewall show rule all verbose}
...
TotalSeconds      : 1.0588127

By commenting out the Get-NetFirewall*Filter part of the script it runs at full speed but of course is missing all the data I want. The idea is to collect detailed info on all firewall rules and then output the lot as JSON.

Does anyone have an idea how to optimize this? I'm a PowerShell noob so I'm hoping I missed something obvious. The complete script is:

Show-NetFirewallRule | `
    Where-Object { $_.cimclass.toString() -eq "root/standardcimv2:MSFT_NetFirewallRule" } | `
        ForEach-Object { `
            $af = $_ | Get-NetFirewallAddressFilter | Select-Object -First 1; # Assumes only one filter
            $appf = $_ | Get-NetFirewallApplicationFilter | Select-Object -First 1; # Assumes only one filter
            $pf = $_ | Get-NetFirewallPortFilter | Select-Object -First 1; # Assumes only one filter
            $if = $_ | Get-NetFirewallInterfaceTypeFilter | Select-Object -First 1; # Assumes only one filter

            New-Object -Type PSCustomObject -Property @{
              Name = $_.Name
              DisplayName = $_.DisplayName
              Description = $_.Description
              Enabled = $_.Enabled.toString()
              Action = $_.Action.toString()
              Direction = $_.Direction.toString()
              EdgeTraversalPolicy = $_.EdgeTraversalPolicy.toString()
              Profile = $_.Profile.toString()
              DisplayGroup = $_.DisplayGroup
              # Address Filter
              LocalAddress = $af.LocalAddress
              RemoteAddress = $af.RemoteAddress
              LocalIp = $af.LocalIp
              RemoteIp = $af.RemoteIp
              # Port Filter
              LocalPort = $pf.LocalPort
              RemotePort = $pf.RemotePort
              Protocol = $pf.Protocol
              IcmpType = $pf.IcmpType
              # Application Filter
              Program = $appf.Program
              # Interface Filter
              InterfaceType = $if.InterfaceType.toString()
            }
        } | Convertto-json

回答1:


This approach is faster, so, maybe a different approach for you to get the same information.

param
( 
    [switch]$Local, 
    [switch]$GPO 
) 

# If no switches are set the script will default to local firewall rules 
if (!($Local) -and !($Gpo)) 
{ $Local = $true } 

$RegistryKeys = @() 

if ($Local) {$RegistryKeys += 'Registry::HKLM\System\CurrentControlSet\Services\SharedAccess\Parameters\FirewallPolicy\FirewallRules'} 
if ($GPO) {$RegistryKeys += 'Registry::HKLM\Software\Policies\Microsoft\WindowsFirewall\FirewallRules'} 

Foreach ($Key in $RegistryKeys) 
{ 
    if (Test-Path -Path $Key) 
    { 
        (Get-ItemProperty -Path $Key).PSObject.Members | 
        Where-Object {
        (@('PSPath','PSParentPath','PSChildName') -notcontains $_.Name) -and 
        ($_.MemberType -eq 'NoteProperty') -and 
        ($_.TypeNameOfValue -eq 'System.String')} | 
         ForEach-Object { 

            # Prepare hashtable 
            $HashProps = @{ 
                NameOfRule = $_.Name 
                RuleVersion = ($_.Value -split '\|')[0] 
                Action = $null 
                Active = $null 
                Dir = $null 
                Protocol = $null 
                LPort = $null 
                App = $null 
                Name = $null 
                Desc = $null 
                EmbedCtxt = $null 
                Profile = $null 
                RA4 = $null 
                RA6 = $null 
                Svc = $null 
                RPort = $null 
                ICMP6 = $null 
                Edge = $null 
                LA4 = $null 
                LA6 = $null 
                ICMP4 = $null 
                LPort2_10 = $null 
                RPort2_10 = $null 
            } 

            # Determine if this is a local or a group policy rule and display this in the hashtable 
            if ($Key -match 'HKLM\\System\\CurrentControlSet') 
            {  $HashProps.RuleType = 'Local' } 
            else 
            {  $HashProps.RuleType = 'GPO' } 

            # Iterate through the value of the registry key and fill PSObject with the relevant data 
            ForEach ($FireWallRule in ($_.Value -split '\|')) 
            { 
                switch (($FireWallRule -split '=')[0]) 
                { 
                    'Action' {$HashProps.Action = ($FireWallRule -split '=')[1]} 
                    'Active' {$HashProps.Active = ($FireWallRule -split '=')[1]} 
                    'Dir' {$HashProps.Dir = ($FireWallRule -split '=')[1]} 
                    'Protocol' {$HashProps.Protocol = ($FireWallRule -split '=')[1]} 
                    'LPort' {$HashProps.LPort = ($FireWallRule -split '=')[1]} 
                    'App' {$HashProps.App = ($FireWallRule -split '=')[1]} 
                    'Name' {$HashProps.Name = ($FireWallRule -split '=')[1]} 
                    'Desc' {$HashProps.Desc = ($FireWallRule -split '=')[1]} 
                    'EmbedCtxt' {$HashProps.EmbedCtxt = ($FireWallRule -split '=')[1]} 
                    'Profile' {$HashProps.Profile = ($FireWallRule -split '=')[1]} 
                    'RA4' {[array]$HashProps.RA4 += ($FireWallRule -split '=')[1]} 
                    'RA6' {[array]$HashProps.RA6 += ($FireWallRule -split '=')[1]} 
                    'Svc' {$HashProps.Svc = ($FireWallRule -split '=')[1]} 
                    'RPort' {$HashProps.RPort = ($FireWallRule -split '=')[1]} 
                    'ICMP6' {$HashProps.ICMP6 = ($FireWallRule -split '=')[1]} 
                    'Edge' {$HashProps.Edge = ($FireWallRule -split '=')[1]} 
                    'LA4' {[array]$HashProps.LA4 += ($FireWallRule -split '=')[1]} 
                    'LA6' {[array]$HashProps.LA6 += ($FireWallRule -split '=')[1]} 
                    'ICMP4' {$HashProps.ICMP4 = ($FireWallRule -split '=')[1]} 
                    'LPort2_10' {$HashProps.LPort2_10 = ($FireWallRule -split '=')[1]} 
                    'RPort2_10' {$HashProps.RPort2_10 = ($FireWallRule -split '=')[1]} 
                    Default {} 
                } 
            } 

            # Create and output object using the properties defined in the hashtable 
            New-Object -TypeName 'PSCustomObject' -Property $HashProps
        } 
    } 
}

# Partial results

Action      : Allow
LPort2_10   : 
RuleType    : Local
LPort       : 135
Edge        : 
LA6         : 
Dir         : In
Desc        : @icsvc.dll,-710
ICMP4       : 
RA4         : 
Name        : @icsvc.dll,-709
LA4         : 
App         : %SystemRoot%\system32\svchost.exe
ICMP6       : 
Protocol    : 6
RuleVersion : v2.0
NameOfRule  : vm-monitoring-dcom
RPort       : 
Svc         : RpcSs
RA6         : 
Profile     : 
EmbedCtxt   : @icsvc.dll,-700
RPort2_10   : 
Active      : FALSE



回答2:


Does Get-NetFirewallRule give you what you want?

$MyRules = Get-NetFirewallRule
foreach ($rule in $MyRules) {
    [the rest of your code]
}



回答3:


Ok thanks heaps for everyone who posted. The original problem was that netsh leaves unresolved names like:

@{Microsoft.Todos_1.41.12842.0_x64__8wekyb3d8bbwe?ms-resource://Microsoft.Todos/Resources/app_name_ms_todo}
@{Microsoft.Todos_1.41.12842.0_x64__8wekyb3d8bbwe?ms-resource://Microsoft.Todos/Resources/app_name_ms_todo}

In its output that can only be resolved by PowerShell, using the original script. The problem with this approach is that this is very slow (minutes).

Suggestions on this thread and from colleagues were:

  • Use PS batching (made things slower - suggesting WMI bottleneck)
  • Read the registry directly (mostly works, but data is left unresolved in different ways and would require slow registry scans to resolve - eg @FirewallAPI.dll,-25427 to whatever resource that references)
  • Use the COM API New-Object -ComObject HNetCfg.FwPolicy (has the same output problems as netsh)
  • Adjust use of pipelines/object creation (no measurable impact)

In summary, the optimization I want is impossible without sacrificing the data that I want. I'm going to attempt to use the faster COM API/netsh most of the time but switch to using the powershell API when there is no choice (when unresolved names force us to)




回答4:


I don't think this is well understood, but the get-netfirewall*filter commands are meant to do things faster. Just compare the first pipeline to the second pipeline. It's like using the -filter option in other commands like get-childitem or get-wmiobject. I don't think even the writers of the online help understand this. It's like powershell is only 80% documented.

Get-NetFirewallRule | 
Get-NetFirewallPortFilter | 
Where LocalPort -eq 3389 | Get-NetFirewallRule | 
Set-NetFirewallRule -RemoteAddress 192.168.1.1 -WhatIf
Get-NetFirewallPortFilter | 
Where LocalPort -eq 3389 | Get-NetFirewallRule | 
Set-NetFirewallRule -RemoteAddress 192.168.1.1 -WhatIf


来源:https://stackoverflow.com/questions/53146884/how-can-i-speed-up-powershell-to-get-firewall-rules-on-windows-10

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!