Managing Windows Firewall Rules with PowerShell, Part 3: Creating a Baseline

[Editor’s Note: This is Part 3 of a three-part series on managing Windows firewall rules with PowerShell.]

In the previous installments of this series, we explored the intricacies of utilizing PowerShell for managing Windows firewall rules and delved into methods for extracting more detailed information than what the Get-NetFirewallRule cmdlet offers. While these techniques are quite effective, there’s an opportunity to elevate your approach further. By leveraging the strategies discussed earlier, you can establish a baseline of your firewall settings and subsequently compare your current configurations against this baseline to identify any discrepancies.

There are numerous tools and methods available for documenting your baseline settings. For this discussion, I will employ a refined version of the technique introduced in Part 2, which involves exporting firewall rules to a CSV file. Below is the modified script:

Get-NetFirewallRule | ForEach-Object {
$Rule = $_
$PortFilter = $Rule | Get-NetFirewallPortFilter
$AddressFilter = $Rule | Get-NetFirewallAddressFilter
[PSCustomObject]@{
    Name          = $Rule.DisplayName
    Direction     = $Rule.Direction
    Action        = $Rule.Action
    Protocol      = $PortFilter.Protocol
    LocalPort     = $PortFilter.LocalPort
    RemotePort    = $PortFilter.RemotePort
    LocalAddress  = $AddressFilter.LocalAddress
    RemoteAddress = $AddressFilter.RemoteAddress
}
} | Export-CSV "C:TempFirewallRules.csv" -NoTypeInformation

This script closely resembles the previous one; however, instead of specifying a single firewall rule, it utilizes the Get-NetFirewallRule cmdlet to retrieve all existing rules. The script employs a ForEach loop to compile each rule’s details into an array of custom objects, which is then exported to a CSV file upon completion of the loop.

You can observe the script and the resulting CSV file in Figure 1. The script has successfully exported the firewall rules to a CSV file.

<h2 class="ContentText ContentTextvarianth2 ContentTextalignleft” data-testid=”content-text” id=”Checking for Changes”>Checking for Changes

It is crucial to recognize that the CSV file created represents the state of your firewall rules at a specific moment. This means you can revisit this file later to compare the current firewall rules against it and detect any changes.

Here is the PowerShell script designed for comparing the two CSV files:

$Baseline = Import-Csv -Path "C:TempFirewallRules.csv"
$Current  = Import-Csv -Path "C:TempCurrentFirewallRules.csv"

foreach ($BaseRule in $Baseline) {
    $MatchExists = $False
    $BaseName = $BaseRule.Name

    ForEach ($Row in $Current) {
        $CurrentRuleName = $Row.Name
        If ($CurrentRuleName -eq $BaseName) {
            Write-Host "Checking rule: $CurrentRuleName" -ForegroundColor White
            $MatchExists = $True
            $FieldsToCompare = "Direction", "Action", "Protocol", "LocalPort", "RemotePort", "LocalAddress", "RemoteAddress"
            $Differences = @()

            ForEach ($Field in $FieldsToCompare) {
                $BaseValue = [string]$BaseRule.$Field
                $CurrentValue = [string]$Row.$Field

                if ($BaseValue -ne $CurrentValue) {
                    $Differences += "Field '$Field' has changed from '$BaseValue' to '$CurrentValue'"
                }
            }

            if ($Differences.Count -gt 0) {
                Write-Host "Rule '$BaseName' has changed:" -ForegroundColor Magenta
                $Differences | ForEach-Object { Write-Host "    $_" -ForegroundColor Cyan}
            }
        }
    }
    If (-Not $MatchExists) {
        Write-Host "The rule '$BaseName' has been deleted from the current firewall configuration." -ForegroundColor Yellow
    }
}

The script begins by defining the paths for both the baseline and current CSV files. It then initiates a ForEach loop to examine all rules within the baseline CSV. For each rule, it sets the $MatchExists variable to False, indicating that no match has yet been found in the CurrentFirewallRules.csv file. A variable named $BaseName is created to store the current rule’s name.

A nested ForEach loop follows, which checks all records in the CurrentFirewallRules.csv file to find a match with the baseline record. Upon finding a match, the script displays the name of the record being checked and sets the $MatchExists variable to True. A third ForEach loop compares the various fields of data associated with the current record and its match to detect any changes.

If changes are identified, they are logged in an array called Differences. The script then outputs a message indicating which firewall rule has been modified, along with the contents of the $Differences array. This array approach is beneficial as a single firewall rule may have multiple modifications, allowing for comprehensive reporting.

Finally, the script checks if the $MatchExists variable remains False, indicating that no matching rule was found, and it will notify that the rule has been deleted. You can see the execution of the script in Figure 2.

While this script is functional, there is potential for enhancement. Currently, it does not account for any new rules created since the baseline was established, nor does it handle cases where multiple rules share the same name, which could lead to confusion and false modification alerts. Should there be sufficient interest, I may develop an updated version of this script capable of performing a two-way comparison between the files and managing duplicate rule names.

Winsage
Managing Windows Firewall Rules with PowerShell, Part 3: Creating a Baseline