Public/Add-MSIXFirewallRule.ps1

function Add-MSIXFirewallRule {
<#$
.SYNOPSIS
    Adds a firewall rule to the AppxManifest.xml of an MSIX package.
 
.DESCRIPTION
    The Add-MSIXFirewallRule function creates a windows.firewallRules extension
    entry in the AppxManifest.xml for a given application. The rule allows
    specifying direction, protocol and port for an executable inside the
    package.
 
.PARAMETER MSIXFolder
    The path to the expanded MSIX package.
 
.PARAMETER MISXAppID
    The AppID of the application to which the rule should be added.
 
.PARAMETER Executable
    Executable file inside the package that the rule applies to.
 
.PARAMETER Direction
    Direction of the firewall rule. Allowed values are 'in' or 'out'.
 
.PARAMETER IPProtocol
    Network protocol for the rule. Allowed values are 'TCP' or 'UDP'.
 
.PARAMETER Port
    Local port to open. The port is used for LocalPortMin and LocalPortMax.
 
.PARAMETER Profile
    Optional firewall profile. Defaults to 'all'.
 
.EXAMPLE
    Add-MSIXFirewallRule -MSIXFolder "C:\MyApp" -MISXAppID "App" -Executable "app.exe" -Direction in -IPProtocol TCP -Port 4810
#>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true, ValueFromPipeline=$true, Position=0)]
        [System.IO.DirectoryInfo]$MSIXFolder,
        [Parameter(Mandatory=$true, Position=1)]
        [Alias('Id')]
        [string]$MISXAppID,
        [Parameter(Mandatory=$true, Position=2)]
        [string]$Executable,
        [Parameter(Mandatory=$true)]
        [ValidateSet('in','out')]
        [string]$Direction,
        [Parameter(Mandatory=$true)]
        [ValidateSet('TCP','UDP')]
        [string]$IPProtocol,
        [Parameter(Mandatory=$true)]
        [int]$Port,
        [string]$Profile = 'all'
    )

    process {
        if (-not (Test-Path (Join-Path $MSIXFolder -ChildPath 'AppxManifest.xml'))) {
            Write-Verbose '[ERROR] The MSIX temporary folder does not exist - skip adding FirewallRule'
            return
        }

        $manifest = New-Object xml
        $manifest.Load((Join-Path $MSIXFolder -ChildPath 'AppxManifest.xml'))

        $nsmgr = New-Object System.Xml.XmlNamespaceManager $manifest.NameTable
        $AppXNamespaces.GetEnumerator() | ForEach-Object { $nsmgr.AddNamespace($_.key, $_.value) }
        $nsmgr.AddNamespace('desktop2', $AppXNamespaces['desktop2'])

        $appNode = $manifest.SelectSingleNode("//ns:Package/ns:Applications/ns:Application[@Id='" + $MISXAppID + "']", $nsmgr)
        if (-not $appNode) {
            Write-Verbose '[ERROR] Application does not exist - skipping adding FirewallRule'
            return
        }

        if (-not $appNode.extensions) {
            $ext = $manifest.CreateElement('Extensions', $AppXNamespaces['ns'])
            $appNode.AppendChild($ext) | Out-Null
        }

        $extensionNode = $manifest.SelectSingleNode("//ns:Application[@Id='" + $MISXAppID + "']/ns:Extensions/desktop2:Extension[@Category='windows.firewallRules']", $nsmgr)
        if (-not $extensionNode) {
            $extensionNode = $manifest.CreateElement('desktop2:Extension', $AppXNamespaces['desktop2'])
            $extensionNode.SetAttribute('Category','windows.firewallRules')
            $manifest.SelectSingleNode("//ns:Application[@Id='" + $MISXAppID + "']/ns:Extensions", $nsmgr).AppendChild($extensionNode) | Out-Null
        }

        $existingRule = $manifest.SelectSingleNode("//desktop2:Extension[@Category='windows.firewallRules']/desktop2:FirewallRules[@Executable='" + $Executable + "']", $nsmgr)
        if ($existingRule) {
            Write-Verbose "[WARNING] A firewall rule for executable '$Executable' already exists - skipping"
            return
        }

        $fwNode = $manifest.CreateElement('desktop2:FirewallRules', $AppXNamespaces['desktop2'])
        $fwNode.SetAttribute('Executable', $Executable)
        $rule = $manifest.CreateElement('desktop2:Rule', $AppXNamespaces['desktop2'])
        $rule.SetAttribute('Direction', $Direction)
        $rule.SetAttribute('IPProtocol', $IPProtocol)
        $rule.SetAttribute('LocalPortMin', $Port)
        $rule.SetAttribute('LocalPortMax', $Port)
        $rule.SetAttribute('Profile', $Profile)
        $fwNode.AppendChild($rule) | Out-Null
        $extensionNode.AppendChild($fwNode) | Out-Null

        $manifest.PreserveWhitespace = $false
        $manifest.Save((Join-Path $MSIXFolder -ChildPath 'AppxManifest.xml'))
    }
}