How to Automate Conversion from App-V to MSIX

Customers that use Microsoft App-V are certainly interested in the potential future of MSIX. While the compatibility levels for typical applications under MSIX is not yet acceptable overall and we will be continuing to leverage App-V for years (decades?) to come, I believe that it is important for them to begin the job of working with MSIX apps when we can.

MSIX eventually will offer a better model for providing applications to end-users in our organizations; but this comes at a cost in needing to develop and learn new packaging, deployment, and support operational procedures.  When software vendors eventually make to the move to MSIX, you will be forced to figure all that out, so my argument is that it is better to start work on that sooner, while it is optional and you can delay implementation when issues pop up, because end-users already have access to the app delivered in a different form. This will be preferable than to be under pressure of delivering a new app with no other option available other than MSIX.

One question I often get from customers is around the possibility of converting existing App-V packages into MSIX directly, rather than re-capturing the vendor installer again.  There is some support built into the Microsoft MSIX Packaging Tool, both through the GUI and a command line interface.  This article looks at using the command line interface to automate the process.

Microsoft has an article outlining the use of the command line interface in a Microsoft Tech Community blog post  including a PowerShell script to perform bulk conversion. I found the details in the script to have some issues that I needed to fix, and while I was there of course I added a few improvements which I can share here.

Before I do, there are a few points to share from my experience that you should understand before taking on such a project.

First, no matter how you create an MSIX package from an existing Win32 or .Net Framework application, we know already that only a portion of them are compatible today.  In the latest MSIX Report Card testing we can see that only 60% of Enterprise applications are suitable for repackaging into MSIX today.  But only 30 % are suitable without the Package Support Framework being added and configured, an open source project for add on code to improve compatibility fixups that allow traditional applications to be remediated to work inside this new container. 

Packaging with the Microsoft MSIX Packaging Tool, either using a vendor install capture or command line interface, does not add this remediation — a significant difference from your experience with App-V where similar capabilities are built-in.  Building the package through a capture of the vendor installer and adding in the PSF, such as by using PsfTooling, allows us to get to that 60%, but there is no option to add the PSF as part of this conversion process.

Microsoft Partners that offer repackaging tools may have workflows that allow the PSF to get injected.  These alternatives come at a cost, however, but if you already own their tools you should investigate their options. 

But the bottom line is that no matter how you convert or migrate, you need to have realistic expectations going into it.  Mass conversion using this technique is easy to do, just be prepared for limited success.

I ran a set of 75 App-V packages using the scripting below.  The script averaged under 2 minutes per package, less than 2 hours using a single VM for this set.  I ended up with 16% of the packages as ultimately acceptable to Enterprise standards. If you want to try it, here is what you’ll need:

  • Start with a VM that you use for Sequencing on Windows 10.
  • Add the Microsoft MSIX Packaging Tool from the Microsoft Store.
  • Create a Code Signing Certificate. See my previous articles On MSIX and Certificates: Part 1 for Developers (advancedinstaller.com)  and MSIX Code Signing Certificates Part 2 – For IT Pros – Confessions of a Guru (tmurgent.com).
  • Copy the PowerShell and XML files below into a folder.
  • Install PassiveInstall an open source PowerShell module which the script above depends on.
  • Get a copy of Signtool.exe. It is part of the Windows SDK, but redistributed through many tools.
  • Edit the top 7 lines of the PowerShell to customize your folder locations for source of your App-V packages, code signing certificate details , and timestamping service preference.
  • Right click on the PowerShell file and select “Run With PowerShell”.  The script will auto elevate (with UAC) and convert all of your packages.  It will also create a log file to capture the results.
  • Browse the log file.  Only packages created without errors and warnings are of interest; any package with warnings should be handled differently than this conversion utility. Those packages will have the following three lines without any lines in between the them:
Ignoring Default Server Port Number attribute on the Settings element as the Remote Machine attribute is not set
Creating MSIX package
Package created under: %UserProfile%\Desktop\Scripted\MSIX\Ace-1903Seq-1909OS-StdTemplate-2_1.0.0.0_x64__xwfzvwzp69w2e.msix

Here is an example of a package with an issue. This one can be solved by packaging differently:

Creating MSIX package
 COM: CLSID {23170f69-40c1-278a-1000-000100020000} cannot be published in the AppX manifest since in-process servers are not usable outside their package. To publish this class, register a DLL surrogate for the in-process server.

Here is an example of a package with an issue due to a limitation in the length of the package name:

MsixPackagingTool.exe : Error reading the template file: Maximum length of PackageName is 50 usable outside their package.
  • Test the packages to ensure the full fidelity of the packaged app is available. In my case, 33% of the packages passed through the screening of the log file output, but of course many of those still need the PSF to be deployable, cutting the final number to 16%.

Here is the PowerShell script:

$AppvContentStore = "\\Server\Packages\AppV"
$PublisherName = "CN=YourComanyName"
$PublisherDisplayName = "YourComany"
$Certificate = "CodeSigningCertificate.pfx"
$CertificatePassword = "CertPassword"
$TimestampingURL = "http://timestamp.digicert.com"
$SignToolPath = "C:\PathTo\signtool.exe"

#----------------------- Modifications should only be needed above this line ---------------------

Import-Module "C:\Program Files\WindowsPowerShell\Modules\PassiveInstall\PassiveInstall.dll"
Approve-PassiveElevation -AsAdmin

$executingScriptDirectory = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent

# Creating a folder to store the template files used for the conversion
$MPTtemplateLocation = ([System.IO.Path]::Combine($executingScriptDirectory, "MPT_Templates"))
New-Item -Force -Type Directory ($MPTtemplateLocation)

# Creating a folder to store the MSIX packages
$SaveLocation = ([System.IO.Path]::Combine($executingScriptDirectory, "MSIX"))
New-Item -Force -Type Directory ($SaveLocation)

# Define and clear out any existing log file
$ConversionMasterLogFile = ([System.IO.Path]::Combine($SaveLocation, "Log.txt"))
if ( (test-path "$($ConversionMasterLogFile)" ) -eq $true) {
    Remove-PassiveFiles $ConversionMasterLogFile
}
Write-host "Logging of this session written to $($ConversionMasterLogFile)"

function Run_MainProcessing($AppVFilePath)
{
    $Installerpath = $AppVFilePath.FullName
    $filename = $AppVFilePath.BaseName
    write-host ""
    write-host -ForegroundColor Cyan "starting the conversion of: " $Installerpath
    
    # MSIX package name cannot contain spaces, dashes or dots, so replacing these
    $packageStrippedName = $filename -replace '\s+', ''  -replace '_', '-'
    $job = "job" + $counter
    
    # get the contents of the template XML
    [String]$newXml = Get-Content -path $executingScriptDirectory\MsixPackagingToolTemplate.xml | Out-String
    # Replace the placeholders with the correct values
    $newXml = $newXml.Replace("[Installer]", "$Installerpath")
    $newXml = $newXml.Replace("[SaveLocation]", "$SaveLocation")
    $newXml = $newXml.Replace("[PackageName]", "$packageStrippedName")
    $newXml = $newXml.Replace("[PackageDisplayName]", "$filename")
    $newXml = $newXml.Replace("[PublisherName]", "$PublisherName")
    $newXml = $newXml.Replace("[PublisherDisplayName]", "$PublisherDisplayName")
        
    # saving the newly created template
    $JobTemplate = "$($MPTtemplateLocation)\MsixPackagingToolTemplate_$($job).xml"
    if ( (test-path "$($JobTemplate)" ) -eq $true) {
        Remove-PassiveFiles "$($JobTemplate)"
    }
    $newXml | out-File "$($JobTemplate)" -Encoding Ascii -Force
     
    # Starting the conversion  NOTE: Packaging tool does not accept putting template filepath in quotes -- so no spaces allowed!!!
    MsixPackagingTool.exe create-package --template $JobTemplate
  
    $counter = $counter + 1
    write-host (Get-Date)
}

# get all the App-V packages from the ContentStore and convert them.
$counter = 1
write-host (Get-Date)
get-childitem $AppvContentStore -recurse | Where-Object { $_.extension -eq ".appv" } | ForEach-Object {
    $err = Run_MainProcessing $_ *>&1
    Write-Output $err >> "$($ConversionMasterLogFile)"
    Write-Output $err
    
}

# App-V packages converted to MSIX. Signing the new MSIX packages
Get-ChildItem $SaveLocation | foreach-object {
    $cmd = "`"" + $SignToolPath + "\signtool.exe`""
    $args = "sign /a /v  /f " + $Certificate + " /p `"" + $CertificatePassword + "`" /fd SHA256 /tr " + $TimestampingURL + " `"" + $MSIXpackage + "`""
    Start-Process $cmd -Wait -ArgumentList  $args 
}
# cleanup
Write-host "Cleaning up packaging tool; ignore any errors..."
MsixPackagingTool.exe cleanup

write-host -ForegroundColor "Green"  "Done."
Show-PassiveTimer 20000 "End of script, Ctrl-C to end now or wait for timer. Left-Click on window to pause."
Start-Sleep 20

And here is the XML file used as a template for the command line conversion tool. There is no need to edit this file.

<MsixPackagingToolTemplate
    xmlns="http://schemas.microsoft.com/appx/msixpackagingtool/template/2018"
    xmlns:mptv2="http://schemas.microsoft.com/msix/msixpackagingtool/template/1904">
   <Installer Path="[Installer]"/>
   <SaveLocation PackagePath="[SaveLocation]" />
   <PackageInformation
       PackageName="[PackageName]"
       PackageDisplayName="[PackageDisplayName]"
       PublisherName="[PublisherName]"
       PublisherDisplayName="[PublisherDisplayName]"
       Version="1.0.0.0">
   </PackageInformation>
</MsixPackagingToolTemplate>

By Tim Mangan

Tim is a Microsoft MVP, and a Citrix CTP Fellow. He is an expert in App-V and MSIX.