Public/New-MSIXApplicationVariant.ps1


function New-MSIXApplicationVariant {
<#
.SYNOPSIS
    Clones an Application entry in AppxManifest.xml under a new Id.

.DESCRIPTION
    Creates a deep copy of an existing Application element and inserts it into the
    Applications section with a new @Id. Optionally overrides the Executable
    attribute and the AppListEntry visibility on the cloned entry.

    Use this to create a second launcher variant for the same executable that
    requires different command-line arguments. After cloning, call
    Add-MSXIXPSFShim on the new Id to wire it through a dedicated PSF launcher
    with the desired -Arguments value.

.PARAMETER MSIXFolder
    Path to the expanded MSIX package folder (must contain AppxManifest.xml).

.PARAMETER SourceAppId
    @Id of the Application entry to clone.

.PARAMETER NewAppId
    @Id to assign to the cloned Application entry. Must be unique within the manifest.

.PARAMETER Executable
    Optional. Overrides the Executable attribute on the cloned entry.
    Useful when the variant should launch a different binary.

.PARAMETER AppListEntry
    Optional. Sets the AppListEntry attribute on the cloned uap:VisualElements element.
    Accepted values: 'default', 'none'.
    Use 'none' to hide the variant from the Start menu (typical for argument-only variants).

.EXAMPLE
    # Create a hidden WinRAR variant for silent operation, then wire it through PSF
    New-MSIXApplicationVariant -MSIXFolder "C:\MSIXTemp\WinRAR" `
        -SourceAppId "WinRAR" -NewAppId "WinRAR_Silent" -AppListEntry none

    Add-MSXIXPSFShim -MSIXFolder "C:\MSIXTemp\WinRAR" `
        -MISXAppID "WinRAR_Silent" -Arguments "-s" -PSFArchitektur x64

.OUTPUTS
    System.String
    The new Application Id ($NewAppId) on success.

.NOTES
    The cloned entry inherits all child elements (Extensions, VisualElements, etc.)
    from the source. Review the manifest after cloning to remove or adjust
    child elements that should not be duplicated (e.g. execution aliases).
    https://www.nick-it.de
    Andreas Nick, 2026
#>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true, Position = 0)]
        [System.IO.DirectoryInfo] $MSIXFolder,

        [Parameter(Mandatory = $true, Position = 1)]
        [String] $SourceAppId,

        [Parameter(Mandatory = $true, Position = 2)]
        [String] $NewAppId,

        [String] $Executable = '',

        [ValidateSet('default', 'none')]
        [String] $AppListEntry = ''
    )

    $manifestPath = Join-Path $MSIXFolder 'AppxManifest.xml'
    if (-not (Test-Path $manifestPath)) {
        Write-Error "AppxManifest.xml not found in: $($MSIXFolder.FullName)"
        return
    }

    $manifest = New-Object xml
    $nsmgr = New-Object System.Xml.XmlNamespaceManager $manifest.NameTable
    $AppXNamespaces.GetEnumerator() | ForEach-Object {
        $nsmgr.AddNamespace($_.Key, $_.Value)
    }
    $manifest.Load($manifestPath)

    $sourceApp = $manifest.SelectSingleNode(
        "//ns:Package/ns:Applications/ns:Application[@Id='$SourceAppId']", $nsmgr)
    if ($null -eq $sourceApp) {
        Write-Error "Application '$SourceAppId' not found in AppxManifest.xml."
        return
    }

    $conflict = $manifest.SelectSingleNode(
        "//ns:Package/ns:Applications/ns:Application[@Id='$NewAppId']", $nsmgr)
    if ($null -ne $conflict) {
        Write-Error "Application '$NewAppId' already exists in AppxManifest.xml."
        return
    }

    $clone = $sourceApp.CloneNode($true)
    $null = $clone.SetAttribute('Id', $NewAppId)

    if ($Executable -ne '') {
        $null = $clone.SetAttribute('Executable', $Executable)
    }

    if ($AppListEntry -ne '') {
        $veNode = $clone.SelectSingleNode('uap:VisualElements', $nsmgr)
        if ($null -ne $veNode) {
            $null = $veNode.SetAttribute('AppListEntry', $AppListEntry)
        }
        else {
            Write-Warning "uap:VisualElements not found on cloned Application '$NewAppId' — AppListEntry not set."
        }
    }

    $applicationsNode = $manifest.SelectSingleNode("//ns:Package/ns:Applications", $nsmgr)
    $null = $applicationsNode.AppendChild($clone)

    $manifest.PreserveWhitespace = $false
    $manifest.Save($manifestPath)

    Write-Verbose "Application '$SourceAppId' cloned as '$NewAppId'."
    Write-Output $NewAppId
}