Test MSIX packages for app attach
This article shows you how to mount MSIX packages outside of Azure Virtual Desktop to help test your packages for app attach. The APIs that power app attach are available for Windows 11 Enterprise and Windows 10 Enterprise. These APIs can be used outside of Azure Virtual Desktop for testing, however there's no management plane for app attach or MSIX app attach outside of Azure Virtual Desktop.
For more information about app attach and MSIX app attach, see app attach and MSIX app attach in Azure Virtual Desktop.
Prerequisites
Before you can test a package to follow the directions in this article, you need the following things:
A device running Windows 11 Enterprise or Windows 10 Enterprise.
An application you expanded from MSIX format into an image you can use with app attach. Learn how to Create an MSIX image to use with app attach in Azure Virtual Desktop.
If you're using a CimFS image, you need to install the CimDiskImage PowerShell module.
A user account that has local administrator permission on the device you're using to test the MSIX package.
You don't need an Azure Virtual Desktop deployment because this article describes a process for testing outside of Azure Virtual Desktop.
Note
Microsoft Support doesn't support CimDiskImage PowerShell module, so if you run into any problems, you'll need to submit a request on the module's GitHub repository.
Phases
To use MSIX packages outside of Azure Virtual Desktop, there are four distinct phases that you must perform in the following order:
- Stage
- Register
- Deregister
- Destage
Staging and destaging are machine-level operations, while registering and deregistering are user-level operations. The commands you need to use vary based on which version of PowerShell you're using and whether your disk images are in CimFS, VHDX or VHD format.
Note
All MSIX packages include a certificate. You're responsible for making sure the certificates for MSIX packages are trusted in your environment.
Prepare to stage an MSIX package
The staging script prepares your machine to receive the MSIX package and mounts the relevant package to your machine.
Select the relevant tab for the version of PowerShell you're using.
To stage packages using PowerShell 6 or later, you need to run the following commands before the staging operations to bring the capabilities of the Windows Runtime package to PowerShell.
Open a PowerShell prompt as an administrator.
Run the following command to download and install the Windows Runtime Package. You only need to run the following commands once per machine.
#Required for PowerShell 6 and later $nuGetPackageName = 'Microsoft.Windows.SDK.NET.Ref' Register-PackageSource -Name MyNuGet -Location https://www.nuget.org/api/v2 -ProviderName NuGet Find-Package $nuGetPackageName | Install-Package
Next, run the following command to make the Windows Runtime components available in PowerShell:
#Required for PowerShell 6 and later $nuGetPackageName = 'Microsoft.Windows.SDK.NET.Ref' $winRT = Get-Package $nuGetPackageName $dllWinRT = Get-ChildItem (Split-Path -Parent $winRT.Source) -Recurse -File WinRT.Runtime.dll $dllSdkNet = Get-ChildItem (Split-Path -Parent $winRT.Source) -Recurse -File Microsoft.Windows.SDK.NET.dll Add-Type -AssemblyName $dllWinRT.FullName Add-Type -AssemblyName $dllSdkNet.FullName
Stage an MSIX package
Now that you prepared your machine to stage MSIX packages, you need to mount your disk image, then finish staging your MSIX package.
Mount a disk image
The process to mount a disk image varies depending on whether you're using the CimFs, VHDX, or VHD format for your disk image. Select the relevant tab for the format you're using.
To mount a CimFS disk image:
In the same PowerShell session, run the following command:
$diskImage = "<Local or UNC path to the disk image>" $mount = Mount-CimDiskImage -ImagePath $diskImage -PassThru -NoMountPath #We can now get the Device Id for the mounted volume, this will be useful for the destage step. $deviceId = $mount.DeviceId Write-Output $deviceId
Keep the variable
$deviceId
. You need this information later in this article.When you're done, proceed to Finish staging a disk image.
Finish staging a disk image
Finally, you need to run the following commands for all image formats to complete staging the disk image. This command uses the $deviceId
variable you created when you mounted your disk image in the previous section.
In the same PowerShell session, retrieve the application information by running the following commands:
$manifest = Get-ChildItem -LiteralPath $deviceId -Recurse -File AppxManifest.xml $manifestFolder = $manifest.DirectoryName
Get the MSIX package full name and store it in a variable by running the following commands. This variable is needed for later steps.
$msixPackageFullName = $manifestFolder.Split('\')[-1] Write-Output $msixPackageFullName
Create an absolute URI for the manifest folder for the Package Manager API by running the following commands:
$folderUri = $maniFestFolder.Replace('\\?\','file:\\\') $folderAbsoluteUri = ([Uri]$folderUri).AbsoluteUri
Use the absolute URI to stage the application package by running the following commands:
$asTask = ([System.WindowsRuntimeSystemExtensions].GetMethods() | Where-Object { $_.ToString() -eq 'System.Threading.Tasks.Task`1[TResult] AsTask[TResult,TProgress](Windows.Foundation.IAsyncOperationWithProgress`2[TResult,TProgress])' })[0] $asTaskAsyncOperation = $asTask.MakeGenericMethod([Windows.Management.Deployment.DeploymentResult], [Windows.Management.Deployment.DeploymentProgress]) $packageManager = New-Object -TypeName Windows.Management.Deployment.PackageManager $asyncOperation = $packageManager.StagePackageAsync($folderAbsoluteUri, $null, "StageInPlace")
Monitor the staging progress for the application package by running the following commands. The time it takes to stage the package depends on its size. The
Status
property of the$stagingResult
variable will beRanToCompletion
when the staging is complete.$stagingResult = $asTaskAsyncOperation.Invoke($null, @($asyncOperation)) while ($stagingResult.Status -eq "WaitingForActivation") { Write-Output "Waiting for activation..." Start-Sleep -Seconds 5 } Write-Output $stagingResult
Once your MSI package is staged, you can register your MSIX package.
Register an MSIX package
To register an MSIX package, run the following commands in the same PowerShell session. This command uses the $msixPackageFullName
variable created in a previous section.
$manifestPath = Join-Path (Join-Path $Env:ProgramFiles 'WindowsApps') (Join-Path $msixPackageFullName AppxManifest.xml)
Add-AppxPackage -Path $manifestPath -DisableDevelopmentMode -Register
Now that your MSIX package is registered, your application should be available for use in your session. You can now open the application for testing and troubleshooting. Once you're finished, you need to deregister and destage your MSIX package.
Deregister an MSIX package
Once you're finished with your MSIX package and are ready to remove it, first you need to deregister it. To deregister the MSIX package, run the following commands in the same PowerShell session. These commands get the disk's DeviceId
parameter again, and remove the package using the $msixPackageFullName
variable created in a previous section.
$appPath = Join-Path (Join-Path $Env:ProgramFiles 'WindowsApps') $msixPackageFullName
$folderInfo = Get-Item $appPath
$deviceId = '\\?\' + $folderInfo.Target.Split('\')[0] +'\'
Write-Output $deviceId #Save this for later
Remove-AppxPackage $msixPackageFullName -PreserveRoamableApplicationData
Destage an MSIX package
Finally, to destage the MSIX package, you need to dismount your disk image, run the following command in the same PowerShell session to ensure that the package isn't still registered for any user. This command uses the $msixPackageFullName
variable created in a previous section.
Remove-AppxPackage -AllUsers -Package $msixPackageFullName -ErrorAction SilentlyContinue
Dismount the disks image
To finish the destaging process, you need to dismount the disks from the system. The command you need to use depends on the format of your disk image. Select the relevant tab for the format you're using.
To dismount a CimFS disk image, run the following commands in the same PowerShell session:
Dismount-CimDiskImage -DeviceId $deviceId
Once you finished dismounting your disks, you've safely removed your MSIX package.
Set up simulation scripts for the MSIX app attach agent
If you want to add and remove MSIX packages to your device automatically, you can use the PowerShell commands in this article to create scripts that run at startup, logon, logoff, and shutdown. To learn more, see Using startup, shutdown, logon, and logoff scripts in Group Policy. You need to make sure that any variables required for each phase are available in each script.
You create a script for each phase:
- The startup script runs the stage process.
- The logon script runs the register process.
- The logoff script runs the deregister process.
- The shutdown script runs the destage process.
Note
You can use task scheduler to run the stage script. To run the script, set the task trigger to When the computer starts and enable Run with highest privileges.
Use packages offline
If you're using packages on devices that aren't connected to the internet, you need to make sure the package licenses are installed on your device to successfully run the app. If your device is online, the required licenses should download automatically.
To install the license files, you need to use a PowerShell script that calls the MDM_EnterpriseModernAppManagement_StoreLicenses02_01
class in the WMI Bridge Provider.
Here's how to set up a license for offline use:
Download the app package, license, and required frameworks from the Microsoft Store for Business. You need both the encoded and unencoded license files. To learn how to download an offline-licensed app, see Distribute offline apps.
Run the following PowerShell commands as an administrator. You can install the license is at the end of the staging phase. You need to edit the following variables:
$contentID
is the ContentID value from the unencoded license file (.xml
). You can open the license file in a text editor of your choice.$licenseBlob
is the entire string for the license blob in the Encoded license file (.bin
). You can open the encoded license file in a text editor of your choice.$namespaceName = "root\cimv2\mdm\dmmap" $className = "MDM_EnterpriseModernAppManagement_StoreLicenses02_01" $methodName = "AddLicenseMethod" $parentID = "./Vendor/MSFT/EnterpriseModernAppManagement/AppLicenses/StoreLicenses" #Update $contentID with the ContentID value from the unencoded license file (.xml) $contentID = "{'ContentID'_in_unencoded_license_file}" #Update $licenseBlob with the entire String in the encoded license file (.bin) $licenseBlob = "{Entire_String_in_encoded_license_file}" $session = New-CimSession #The final string passed into the AddLicenseMethod should be of the form <License Content="encoded license blob" /> $licenseString = '<License Content='+ '"' + $licenseBlob +'"' + ' />' $params = New-Object Microsoft.Management.Infrastructure.CimMethodParametersCollection $param = [Microsoft.Management.Infrastructure.CimMethodParameter]::Create("param",$licenseString ,"String", "In") $params.Add($param) try { $instance = New-CimInstance -Namespace $namespaceName -ClassName $className -Property @{ParentID=$parentID;InstanceID=$contentID} $session.InvokeMethod($namespaceName, $instance, $methodName, $params) } catch [Exception] { Write-Host $_ | Out-String }
Demonstration scripts
You can find demonstration scripts for all four stages of testing MSIX packages and syntax help for how to use them in our GitHub repository. These scripts work with any version of PowerShell and any disk image format.
Next steps
Learn more about app attach and MSIX app attach in Azure Virtual Desktop: