Firewall Rules With Environment Variables
April 2021
It's a fairly common request for clients to want a Windows Firewall rule added to an application package. Most of the time they could add it via group policy, but many prefer to keep them with the installer. Going way back the usual way of doing that is with a command like this:
NetSh.exe advfirewall firewall add rule name="Google Chrome" dir=in action=allow protocol=tcp program="C:\Program Files (x86)\google\chrome\application\chrome.exe"
Most of the time that's fine, but what if you want to use an environment variable in the rule? Many of the rules that come with Windows start with %SystemRoot% or %ProgramFiles(x86)% so it's clearly possible. The problem is that when NetSh.exe sees an environment variable, it expands it to its value. So all we need is a different way to set it that takes the environment variable literally.
The New-NetFirewallRule PowerShell command does this right, and in many situations that'll be all you need to know. If you're using PowerShell Application Deployment Toolkit as an installation wrapper then that's the way to go. But Microsoft Installer natively supports VBScript with its own custom action types, so for embedding in a MSI or MST a VBS solution would be better.
By the way, there's been a bit of a misunderstanding by some that VBScript is no longer supported by Microsoft. It's no longer supported in Internet Explorer as a web scripting language, which has nothing to do with support in the Windows Script Host built into Win 10 for admin tasks. So use whatever is going to give the best solution for what you're doing.
Microsoft have an API for administering Windows Firewall, and luckily enough the documentation for it includes VBS examples. It turns out this also accepts literal environment variables, so I've put together the following short script to set firewall rules.
On Error Resume Next
Dim oPolicy : Set oPolicy = CreateObject("HNetCfg.FwPolicy2")
oPolicy.Rules.Remove "Microsoft Teams"
oPolicy.Rules.Remove "Microsoft Teams"
AddFirewallRule "", "Microsoft Teams", "In", "TCP", "%ProgramFiles(x86)%\google\chrome\application\chrome.exe"
AddFirewallRule "", "Microsoft Teams", "In", "UDP", "%ProgramFiles(x86)%\google\chrome\application\chrome.exe"
Sub AddFirewallRule(sGroup, sName, sDirection, sProtocol, sExePath)
If UCase(sDirection) = "IN" Then sDirection = 1
If UCase(sDirection) = "OUT" Then sDirection = 2
If UCase(sProtocol) = "TCP" Then sProtocol = 6
If UCase(sProtocol) = "UDP" Then sProtocol = 17
Dim oRule : Set oRule = CreateObject("HNetCfg.FwRule")
oRule.Enabled = True
oRule.Grouping = sGroup
oRule.Name = sName
oRule.ApplicationName = sExePath
oRule.Protocol = sProtocol
oRule.Direction = sDirection
oPolicy.Rules.Add oRule
End Sub
That's all there is to it. Typically for an API it leaves more for you to do yourself than an exe like NetSh.exe does. If the rule name you're adding already exists you end up with a duplicate entry, so it's important to remove that rule name first. Also, it only removes the first rule it finds with that name, unlike NetSh.exe which removes all of them. So that's why the Remove method is called twice when we're adding both TCP and UDP rules.
You could obviously do a lot more with this. Enumerating existing rules and automating the removal of those with the same name wouldn't be hard to do. Also you occasionally get firewall rules that aren't just inbound TCP and UDP, needing a port specified or something like that. Working from the API doco it's pretty simple to add that sort of functionality. But the most likely way the script's going to be used is from a MSI custom action, and for that short and simple is best.
Send us your comments and feedback on this blog post.© Fenix Consulting Pty Ltd







