Troubleshooting

How to Audit Windows Server 2025 Firewall Rules with PowerShell

11 min read

The Back Room Tech is reader-supported. We may earn a commission when you buy through links on our site. Learn more.

Before you change a firewall rule, capture what Windows Server 2025 reports. PowerShell shows applied rules, their ports, addresses, programs, and policy sources. You can save the results as a local CSV.

This audit reads policy and writes one report file. It doesn’t change firewall settings. That helps when a change ticket asks what a rule does, but its name tells you little.

What Is NetSecurity?

NetSecurity is the Windows PowerShell module for Windows Firewall policy. Its Get- commands read profiles, rules, and the filters that set each rule’s conditions. Windows Server 2025 includes it, so you don’t need an installer.

A rule listing shows whether a rule is enabled, its direction and action, and its profiles. Ports, addresses, and programs sit in related filter objects. You’ll need those filters for a useful inventory.

Prerequisites

  • A Windows Server 2025 computer you can access through its console or an existing desktop session.
  • An account that can read firewall policy. Use an approved administrator account if your organization restricts policy queries.
  • Windows PowerShell 5.1, included with Windows Server 2025. The commands use its built-in NetSecurity module.
  • An existing, writable local folder for the CSV. You’ll enter its full path. Don’t use a network share for this local audit.
  • A defined scope: the server, capture time, and change or incident ticket that will receive the findings.

Run every command on the server you’re auditing. You don’t need another package, special hardware, or PowerShell remoting. If the server connects through a 2.5GbE switch or an upstream firewall, note those devices in the ticket. This report covers the Windows host’s policy.

Step-by-Step Guide

Step 1: Open Windows PowerShell and confirm NetSecurity is available

On a Windows Server desktop, select Start, type Windows PowerShell, and open it. On Server Core, open a local console and enter powershell.exe. Keep the session open through the export; later steps use variables set here.

Check that PowerShell can find the module and rule command:

Get-Module -ListAvailable NetSecurity |
    Select-Object Name, Version, Path

Get-Command Get-NetFirewallRule |
    Select-Object Name, Source

Expected result: The first query lists NetSecurity, its version, and its path. The second shows Get-NetFirewallRule with NetSecurity as its source. Versions and paths vary, so record what your server shows.

If neither query finds NetSecurity, confirm you’re running PowerShell on the Windows Server 2025 machine you intend to audit.

Step 2: Choose and verify a local report folder

In File Explorer, find an existing local folder where your account can save files. Copy its full path from the address bar. Enter that path when PowerShell prompts you:

$reportDirectory = Read-Host 'Enter the full path to an existing writable local folder'

if (-not (Test-Path -LiteralPath $reportDirectory -PathType Container)) {
    throw "The folder does not exist: $reportDirectory"
}

Get-Item -LiteralPath $reportDirectory |
    Select-Object FullName

Expected result: FullName shows the folder you chose. If you see “The folder does not exist,” check the path and repeat this step. Test-Path checks that the folder exists; it doesn’t check write access. The export will report a permission error if your account can’t save there.

Keep the report in an approved location. Firewall inventories can expose program paths, addresses, and policy sources. If your process requires a copy on a NAS or shared archive, follow your normal access rules.

Step 3: Read the three firewall profile settings

Profile defaults help you interpret individual rules. Read the applied settings:

Get-NetFirewallProfile -PolicyStore ActiveStore |
    Format-Table Name, Enabled, DefaultInboundAction, DefaultOutboundAction -AutoSize

Expected result: A table with Domain, Private, and Public rows. Record the server’s values for Enabled, DefaultInboundAction, and DefaultOutboundAction. Don’t assume they match another server.

Local Windows PowerShell showing the three firewall profile rows and the Name, Enabled, DefaultInboundAction, and DefaultOutboundAction columns

To see the network categories reported for the server’s connections, run:

Get-NetConnectionProfile |
    Select-Object InterfaceAlias, NetworkCategory

Expected result: Each reported connection has an interface alias and network category. Compare the category with a rule’s Profile condition. A rule that names Public doesn’t apply to every connection just because it appears in the list.

Step 4: Inventory rules from ActiveStore

Query the applied rules:

Get-NetFirewallRule -PolicyStore ActiveStore |
    Select-Object Name, DisplayName, Enabled, Direction, Action,
        Profile, DisplayGroup, PolicyStoreSource, PolicyStoreSourceType

Expected result: Rule objects with the selected fields. This list can be long. While exploring, append | Select-Object -First 20 for a shorter view. Use the full query for the report.

Legible subset of the local ActiveStore rule listing with Name, DisplayName, Enabled, Direction, Action, Profile, PolicyStoreSource, and PolicyStoreSourceType visible

Why specify ActiveStore? An unqualified Get-NetFirewallRule query reads the persistent store. That helps when you need locally stored policy. ActiveStore also shows rules from policy stores that apply to the computer, so it’s the right starting point here. A rule in ActiveStore can still be disabled or outside a connection’s profile.

Use Name as the rule identifier in notes and follow-up queries. DisplayName is the readable label and can change with language. Record both so reviewers can find the rule and read its label.

PolicyStoreSource and PolicyStoreSourceType show where an applied rule came from. Keep both fields before proposing a change. A rule supplied by another policy source may need a change there.

An illustrative finding looks like this:

Name=<rule identifier>; Enabled=<reported value>; Action=<reported value>; PolicyStoreSource=<reported source>.

Fill the brackets from your result. They aren’t output from a real server.

Step 5: Inspect a rule’s port, address, and application filters

The rule list leaves out some match conditions. Choose one Name from Step 4 and enter it when prompted:

$ruleName = Read-Host 'Enter the Name of a rule from the ActiveStore listing'

$selectedRule = Get-NetFirewallRule -PolicyStore ActiveStore -Name $ruleName -ErrorAction Stop

$selectedRule |
    Select-Object Name, DisplayName, Enabled, Direction, Action,
        Profile, PolicyStoreSource, PolicyStoreSourceType

$selectedRule |
    Get-NetFirewallPortFilter |
    Select-Object Protocol, LocalPort, RemotePort

$selectedRule |
    Get-NetFirewallAddressFilter |
    Select-Object LocalAddress, RemoteAddress

$selectedRule |
    Get-NetFirewallApplicationFilter |
    Select-Object Program

Expected result: First, you’ll see the rule’s identity and status. Separate results show its protocol and ports, addresses, and program condition. Any means that filter doesn’t narrow the condition to a specific value. It doesn’t mean the connection will be allowed.

For a finding, record the values you see:

Name=<rule identifier>; LocalPort=<port or Any>; RemoteAddress=<address or Any>; Program=<path or Any>; PolicyStoreSource=<reported source>.

If PowerShell can’t find the rule, check that you entered its Name instead of its DisplayName. Keep -PolicyStore ActiveStore in the query.

Step 6: Export a readable local CSV

This command reads each ActiveStore rule and its related filters. It puts their values in one CSV row per rule and writes the file to the folder from Step 2. It doesn’t update firewall policy.

if (-not (Test-Path -LiteralPath $reportDirectory -PathType Container)) {
    throw "The report folder is unavailable: $reportDirectory"
}

$reportPath = Join-Path -Path $reportDirectory -ChildPath (
    'WindowsServer2025-FirewallRules-{0}.csv' -f (Get-Date -Format 'yyyyMMdd-HHmmss')
)

$rules = @(Get-NetFirewallRule -PolicyStore ActiveStore -ErrorAction Stop)

$rules |
    ForEach-Object {
        $rule = $_
        $portFilters = @($rule | Get-NetFirewallPortFilter)
        $addressFilters = @($rule | Get-NetFirewallAddressFilter)
        $applicationFilters = @($rule | Get-NetFirewallApplicationFilter)

        [pscustomobject]@{
            Name                  = $rule.Name
            DisplayName           = $rule.DisplayName
            Enabled               = $rule.Enabled
            Direction             = $rule.Direction
            Action                = $rule.Action
            Profile               = $rule.Profile
            Protocol              = ($portFilters.Protocol -join '; ')
            LocalPort             = ($portFilters.LocalPort -join '; ')
            RemotePort            = ($portFilters.RemotePort -join '; ')
            LocalAddress          = ($addressFilters.LocalAddress -join '; ')
            RemoteAddress         = ($addressFilters.RemoteAddress -join '; ')
            Program               = ($applicationFilters.Program -join '; ')
            PolicyStoreSource     = $rule.PolicyStoreSource
            PolicyStoreSourceType = $rule.PolicyStoreSourceType
        }
    } |
    Export-Csv -LiteralPath $reportPath -NoTypeInformation -Encoding UTF8 -NoClobber

Get-Item -LiteralPath $reportPath |
    Select-Object FullName, Length, LastWriteTime

-PolicyStore ActiveStore selects the applied policy view. The three filter commands get conditions kept outside the basic rule object. -join '; ' puts multiple values in one readable CSV cell. -NoTypeInformation removes an unneeded type header. -Encoding UTF8 helps preserve names, and -NoClobber stops an overwrite if the file name already exists.

This export covers port, address, and program conditions only. Service, interface, interface-type, and security conditions live in other filters. Query them with Get-NetFirewallServiceFilter, Get-NetFirewallInterfaceFilter, Get-NetFirewallInterfaceTypeFilter, and Get-NetFirewallSecurityFilter when a rule needs a closer look.

Expected result: Get-Item shows the CSV’s local path, byte length, and write time. Export-Csv prints no success message. If you get access denied, choose another existing writable local folder. Repeat Step 2, then run this step again.

Step 7: Verify the report and review broad inbound allows

Check that the CSV opens and its row count matches the rules captured for export:

$report = @(Import-Csv -LiteralPath $reportPath)

[pscustomobject]@{
    ReportPath    = $reportPath
    RuleCount     = $rules.Count
    CsvRowCount   = $report.Count
    CountsMatch   = ($rules.Count -eq $report.Count)
}

Expected result: CountsMatch is True. Counts depend on the server. If they differ, keep the session open and investigate the export before handing it off.

Open the file locally in a CSV viewer or spreadsheet. Check that Name, LocalPort, RemoteAddress, Program, and PolicyStoreSource share a row for each rule.

Locally opened firewall CSV showing rule identity beside port, address, application, and policy source columns

Next, find candidates for review: enabled inbound allow rules with a broad remote address, local port, or program condition.

$report |
    Where-Object {
        $_.Enabled -eq 'True' -and
        $_.Direction -eq 'Inbound' -and
        $_.Action -eq 'Allow' -and
        (
            $_.RemoteAddress -eq 'Any' -or
            $_.LocalPort -eq 'Any' -or
            $_.Program -eq 'Any'
        )
    } |
    Select-Object Name, DisplayName, Profile, Protocol, LocalPort,
        RemoteAddress, Program, PolicyStoreSource, PolicyStoreSourceType |
    Format-List

Expected result: Zero or more candidate rules. Zero results don’t prove the firewall is safe; this filter has a narrow scope. For each result, check its other CSV fields and the relevant profile defaults. A broad RemoteAddress with a narrow port and program has less scope than a rule with Any in several fields.

Don’t delete or disable a rule based on this list. Put its Name, observed conditions, and policy source in the change request for review.

Configuration: How to Interpret the Inventory

These fields answer different questions. Read them together before you describe what a rule permits.

Field or settingWhat it tells youWhat to check next
NameThe rule identifier for follow-up queriesRecord it exactly, even if DisplayName changes by language
DisplayNameA readable label for reviewersUse it for context, not as the sole identifier
Enabled, Direction, ActionWhether a rule is enabled and the action it requests for its directionCheck the rule’s other conditions and relevant profile
ProfileThe profiles named by the ruleCompare with the connection’s reported network category
Port, address, and application filtersProtocol, endpoints, ports, and program restrictionsRead them together; one broad field doesn’t erase other limits
PolicyStoreSource, PolicyStoreSourceTypeReported source of an applied ruleSend a proposed change to the owner of that policy source
Profile default actionsWhat the profile specifies when no applicable rule determines the actionInclude them when assessing a connection

Rule applicability and precedence matter too. An ActiveStore rule may be disabled or outside the connection’s profile. Its port, address, program, or other conditions may also limit it. The result for a connection depends on all applicable policy.

An inbound allow rule doesn’t prove internet reachability. The service must listen on the right address and port, the rule must apply, and upstream controls must pass the traffic. An edge firewall, router, or network policy can still block it. Check the listening service and external reachability as separate, approved investigations.

Tips and Troubleshooting

Ports or program paths are missing from Get-NetFirewallRule

Cause: The basic rule listing leaves out those filter conditions.

Fix: Use Get-NetFirewallPortFilter, Get-NetFirewallAddressFilter, and Get-NetFirewallApplicationFilter as shown in Step 5. Use the Step 6 CSV when you need those fields beside every rule.

A rule appears in ActiveStore but not in the ordinary rule listing

Cause: An unqualified Get-NetFirewallRule query uses the persistent store. ActiveStore includes rules from policy stores that apply to the computer.

Fix: Query the rule with -PolicyStore ActiveStore. Then check PolicyStoreSource and PolicyStoreSourceType before deciding where a change belongs.

The firewall console’s Monitoring view shows fewer rules

Cause: Monitoring focuses on currently active rules. ActiveStore can include disabled rules or rules outside the profile in use.

Fix: Compare Enabled, Profile, Direction, and Action with the profile and connection data from Step 3. Microsoft’s firewall troubleshooting guidance explains more about the console and policy.

Export-Csv reports that the path cannot be found or access is denied

Cause: The folder no longer exists, the path is wrong, or your account can’t write there.

Fix: Check an existing local folder in File Explorer and copy its full path. Repeat Step 2, then rerun Step 6. The export will generate a new file name.

Two rules have similar names

Cause: DisplayName is a readable label and can change with language. Similar labels don’t prove two entries are the same rule.

Fix: Compare the exact Name values, policy sources, and filters. Put both Name and DisplayName in the change record.

An allow rule looks broad, but the service is unreachable

Cause: The rule may not apply to the connection. The service may also be stopped, or an upstream firewall may block traffic.

Fix: Check the profile and all rule filters first. Then check the service and network path under your normal process. The CSV records policy entries; it doesn’t test connections.

Wrapping Up

StepActionApplies To
1–3Confirm NetSecurity, choose a local folder, and read profile settingsLocal Windows Server 2025
4–6Inspect ActiveStore rules and export rules with their filtersFirewall policy inventory
7Verify the CSV and flag broad inbound allow candidatesChange review

You now have a local CSV of applied rules, their port, address, and program conditions, and their policy sources. Attach it to the change ticket. PowerShell puts those fields in one report, but you’ll still need a separate test to prove internet reachability.

Official Resources