Hey, Scripting Guy! Weekend Scripter: Improving Yesterday’s Windows Firewall Script

Hey, Scripting Guy! Weekend Scripter: Improving Yesterday’s Windows Firewall Script

  • Comments 2
  • Likes

 

Microsoft Scripting Guy Ed Wilson here. The Smokey Mountains are about 10 degrees cooler than Charlotte, North Carolina, is. The problem is that it was nearly 100 degrees Fahrenheit in Charlotte. Anyway, getting to spend some time with my old high school friend has been fun, even if he does not know Windows PowerShell from a seashell. Oh well, he still has good taste in music.

On the drive up into the mountains, the Scripting Wife was commenting on the beautiful trees, creeks, and occasional wild animal; I, on the other hand, was thinking of ways to improve my firewall script.

When we arrived at the cabin John had not yet arrived, so I got out my laptop and got to work on my improved firewall script. The Get-EnabledFireWallRules.ps1 script uses an enumeration to parse the protocol instead of displaying a protocol number like the script from yesterday did. In addition, the Format-Table cmdlet uses a hash table to perform the lookup for the enumeration value as well as to interpret the direction of the rule. The complete Get-EnabledFireWallRules.ps1 script is shown here.

Get-EnabledFireWallRules.ps1

Function New-ProtocolEnum
{
$enum = "
namespace myspace 

public enum protocol

HOPOPT = 0, ICMPv4 = 1, IGMP = 2, TCP = 6, UDP = 17, IPv6 = 41,
IPv6Route = 43, IPv6Frag = 44 ,GRE = 47, ICMPv6 = 58, IPv6NoNxt = 59,
IPv6Opts = 60, VRRP = 112, PGM = 113, L2TP = 115
}
}
"
Add-Type -TypeDefinition $enum -Language CSharpVersion3
} #end function New-ProtocolEnum
Function Test-LoadedEnum
{
Param([string]$enum)
Try
{ 
[reflection.assembly]::GetAssembly([type]$enum) | out-null
New-Object psobject -Property `
@{ "Name" = $enum.tostring() ; "Loaded" = [bool]$true }
}
Catch [system.exception]
{
New-Object psobject -Property `
@{ "Name" = $enum.tostring() ; "Loaded" = [bool]$false }
} 
} #end function Test-LoadedEnum
Function import-Enum
{
Param($rtn)
If ($rtn.Loaded)
{ "$($rtn.name) is loaded" }
else { 
"$($rtn.name) NOT loaded. Loading ..." 
New-ProtocolEnum 
}
} #end function import-enum
Function Get-FireWallRules
{
$fw = New-Object -ComObject hnetcfg.fwpolicy2
$currentProfile = $fw.CurrentProfileTypes
$fw.Rules |
Where-Object {$_.enabled -AND $_.Profiles -eq $currentProfile} | 
Sort-Object -Property direction | 
Format-Table -Property localports,
@{LABEL="Protocol"; EXPRESSION={[enum]::parse([type]"myspace.protocol",$_.protocol)} },
@{LABEL="Direction"; EXPRESSION={ SWITCH($_.direction){ 1{"in"} 2{"out"}} } },
name -autosize
} #end function Get-FirewallRules
# *** ENTRY POINT TO SCRIPT ***
import-enum -rtn (test-LoadedEnum -enum "myspace.protocol")
Get-FireWallRules

The New-ProtocolEnum function is used to create the myspace.protocol enumeration. The creation of new enums was discussed in a Weekend Scripter article a couple of weeks ago. The mapping of the protocol numbers to names is discussed in a TechNet article in the Library. The New-ProtocolEnum function is shown here:

 

Function New-ProtocolEnum
{
$enum = "
namespace myspace 

public enum protocol

HOPOPT = 0, ICMPv4 = 1, IGMP = 2, TCP = 6, UDP = 17, IPv6 = 41,
IPv6Route = 43, IPv6Frag = 44 ,GRE = 47, ICMPv6 = 58, IPv6NoNxt = 59,
IPv6Opts = 60, VRRP = 112, PGM = 113, L2TP = 115
}
}
"
Add-Type -TypeDefinition $enum -Language CSharpVersion3
} #end function New-ProtocolEnum 

The Test-LoadedEnum function is lifted from a recent Weekend Scripter article that I wrote while in Hilton Head, South Carolina. See that article for a discussion of how it works. The complete Test-LoadedEnum function is shown here:

 

Function Test-LoadedEnum
{
Param([string]$enum)
Try
{ 
[reflection.assembly]::GetAssembly([type]$enum) | out-null
New-Object psobject -Property `
@{ "Name" = $enum.tostring() ; "Loaded" = [bool]$true }
}
Catch [system.exception]
{
New-Object psobject -Property `
@{ "Name" = $enum.tostring() ; "Loaded" = [bool]$false }
} 
} #end function Test-LoadedEnum

I decided to write a function to determine if I need to load the enumeration or not. This removed some of the logic from the beginning of the script. In addition, it provides a completely re-usable function. The Import-Enum function is shown here:

 

Function import-Enum
{
Param($rtn)
If ($rtn.Loaded)
{ "$($rtn.name) is loaded" }
else { 
"$($rtn.name) NOT loaded. Loading ..." 
New-ProtocolEnum 
}
} #end function import-enum

The Get-FireWallRules function is much like the script from yesterday’s post. The difference is that it tests for the current profile and uses two hash tables to create customized table entries. The one that does the protocol lookup from the myspace.protocol enum is shown here:

@{LABEL="Protocol"; EXPRESSION={[enum]::parse([type]"myspace.protocol",$_.protocol)} }

The key was using the system.enum static parse method, the type constraint for the myspace.protocol enum, and passing the protocol number. This technique was discussed in the working with enumerations and values Weekend Scripter article. The complete Get-FirewallRules function is shown here:

 

Function Get-FireWallRules
{
$fw = New-Object -ComObject hnetcfg.fwpolicy2
$currentProfile = $fw.CurrentProfileTypes
$fw.Rules |
Where-Object {$_.enabled -AND $_.Profiles -eq $currentProfile} | 
Sort-Object -Property direction | 
Format-Table -Property localports,
@{LABEL="Protocol"; EXPRESSION={[enum]::parse([type]"myspace.protocol",$_.protocol)} },
@{LABEL="Direction"; EXPRESSION={ SWITCH($_.direction){ 1{"in"} 2{"out"}} } },
name -autosize
} #end function Get-FirewallRules

The entry point to the script calls the Import-Enum function while passing the result from the Test-LoadedEnum function. Next, it calls the Get-FirewallRules function. This is shown here:

 

import-enum -rtn (test-LoadedEnum -enum "myspace.protocol")
Get-FireWallRules

When the Get-EnabledFireWallRules.ps1 script runs, the output is displayed that is shown in the following image.

Image of output displayed when script is run

The cool thing about all this is that it provides an excellent example for using our recent discussion of working with enums. Well, I think I hear the “old folks” stirring inside the cabin. I believe I overheard Becky and the Scripting Wife talking about making pancakes, so perhaps I will go inside and see if they need a taste tester. I hope you have a great weekend.

If you want to know exactly what we will be looking at tomorrow, follow us on Twitter or Facebook. If you have any questions, send e-mail to us at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

 

Ed Wilson and Craig Liebendorfer, Scripting Guys

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • There is a mistake: '$_.Profiles -eq $currentProfile'.

    Any of firewall rules can belong to a number of policy profiles (Domain = 1; Private = 2; Public = 4). So the code should be corrected like this: '$_.Profiles -band $currentProfile'. This code checks the occurance of profile type id in 'Profiles' property bitmask.

    By the way, the returned 'CurrentProfiles' bitmask can have more than 1 bit set if multiple profiles are active or current at the same time. Our code correctly handles this situation.

  • Is the webpage inserting a space that is killing the script?

    PS C:\Windows\system32> C:\Windows\System32\get-enabledfirewallrules.ps1

    New-Object : Cannot bind parameter 'Property'. Cannot convert the " " value of type "System.String" to type "System.Collections.Hashtable".

    At C:\Windows\System32\get-enabledfirewallrules.ps1:27 char:30

    + New-Object psobject -Property <<<<  `

       + CategoryInfo          : InvalidArgument: (:) [New-Object], ParameterBindingException

       + FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Microsoft.PowerShell.Commands.NewObjectCommand

    NOT loaded. Loading ...