Help us improve
Share bugs, ideas, or general feedback.
From dotnet-skills
Packaging MSIX apps. Creation, signing, Store submission, App Installer sideload, auto-update.
npx claudepluginhub wshaddix/dotnet-skillsHow this skill is triggered — by the user, by Claude, or both
Slash command
/dotnet-skills:dotnet-msixThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
MSIX packaging pipeline for .NET desktop applications: package creation from csproj (`WindowsPackageType`) and WAP projects, certificate signing (self-signed for development, trusted CA for production, Microsoft Store signing), distribution channels (Microsoft Store submission, App Installer sideloading, enterprise deployment via SCCM/Intune), auto-update configuration (App Installer XML, versi...
Packages WinUI 3 apps into MSIX installers, handles certificate generation/trust, code signing, self-contained deployment, and CI/CD. Use for release preparation or Store submission.
Publishing to package managers. Homebrew, apt/deb, winget, Scoop, Chocolatey manifests.
Guides publishing WPF apps with deployment methods (framework-dependent, self-contained, single-file) and installer options (Velopack, MSIX, NSIS, Inno Setup). Useful for publish, deploy, release, packaging queries.
Share bugs, ideas, or general feedback.
MSIX packaging pipeline for .NET desktop applications: package creation from csproj (WindowsPackageType) and WAP projects, certificate signing (self-signed for development, trusted CA for production, Microsoft Store signing), distribution channels (Microsoft Store submission, App Installer sideloading, enterprise deployment via SCCM/Intune), auto-update configuration (App Installer XML, version checking, differential updates), MSIX bundle format for multi-architecture (.msixbundle), and CI/CD MSIX build steps.
Version assumptions: Windows App SDK 1.6+ (current stable). Windows 10 build 19041+ minimum for MSIX with Windows App SDK. Windows 10 build 1709+ for App Installer auto-update protocol. .NET 8.0+ baseline.
Scope boundary: This skill owns the MSIX packaging pipeline -- creation, signing, distribution, Store submission, auto-update, and CI build steps. WinUI 3 project setup, XAML patterns, and the packaged vs unpackaged deployment mode comparison are owned by [skill:dotnet-winui]. General Native AOT compilation (PublishAot, ILLink descriptors) is owned by [skill:dotnet-native-aot]. General CI/CD pipeline patterns are owned by [skill:dotnet-gha-patterns] and [skill:dotnet-ado-patterns].
Out of scope: WinUI 3 project setup and MSIX vs unpackaged comparison -- see [skill:dotnet-winui]. Native AOT MSBuild configuration -- see [skill:dotnet-native-aot]. General CI/CD pipeline patterns -- see [skill:dotnet-gha-patterns] and [skill:dotnet-ado-patterns]. General NuGet packaging -- see [skill:dotnet-nuget-authoring]. Container-based deployment -- see [skill:dotnet-containers].
Cross-references: [skill:dotnet-winui] for WinUI project setup and packaging mode comparison, [skill:dotnet-native-aot] for AOT + MSIX scenarios, [skill:dotnet-gha-patterns] for CI pipeline structure, [skill:dotnet-ado-patterns] for ADO pipeline structure, [skill:dotnet-nuget-authoring] for NuGet packaging.
Modern WinUI 3 and Windows App SDK apps can produce MSIX packages directly from the application .csproj without a separate Windows Application Packaging (WAP) project.
<!-- MyApp.csproj -->
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
<UseWinUI>true</UseWinUI>
<!-- MSIX packaging mode -->
<WindowsPackageType>MSIX</WindowsPackageType>
<!-- Package identity -->
<AppxPackageDir>$(SolutionDir)AppPackages\</AppxPackageDir>
<GenerateAppxPackageOnBuild>false</GenerateAppxPackageOnBuild>
</PropertyGroup>
</Project>
Build the MSIX package:
# Build MSIX package
dotnet publish --configuration Release --runtime win-x64 \
/p:GenerateAppxPackageOnBuild=true \
/p:AppxPackageSigningEnabled=false
# Output: AppPackages\MyApp_1.0.0.0_x64.msix
For non-WinUI desktop apps (WPF, WinForms), use a Windows Application Packaging Project to wrap the existing app as MSIX. WAP projects (.wapproj) are created via the Visual Studio "Windows Application Packaging Project" template -- they use a specialized project format, not the standard Microsoft.NET.Sdk.
The key configuration is referencing the desktop app project:
<!-- MyApp.Package.wapproj (created via VS template) -->
<!-- Key elements in the generated .wapproj file: -->
<ItemGroup>
<!-- Reference the desktop app project to include in MSIX -->
<ProjectReference Include="..\MyWpfApp\MyWpfApp.csproj" />
</ItemGroup>
<!-- Set target platform versions in the .wapproj PropertyGroup -->
<PropertyGroup>
<TargetPlatformVersion>10.0.22621.0</TargetPlatformVersion>
<TargetPlatformMinVersion>10.0.19041.0</TargetPlatformMinVersion>
<DefaultLanguage>en-US</DefaultLanguage>
<AppxPackageDir>$(SolutionDir)AppPackages\</AppxPackageDir>
</PropertyGroup>
The manifest defines identity, capabilities, and visual assets:
<?xml version="1.0" encoding="utf-8"?>
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
IgnorableNamespaces="uap rescap">
<Identity Name="MyCompany.MyApp"
Publisher="CN=My Company, O=My Company, L=Seattle, S=WA, C=US"
Version="1.0.0.0"
ProcessorArchitecture="x64" />
<Properties>
<DisplayName>My App</DisplayName>
<PublisherDisplayName>My Company</PublisherDisplayName>
<Logo>Assets\StoreLogo.png</Logo>
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Desktop"
MinVersion="10.0.19041.0"
MaxVersionTested="10.0.22621.0" />
</Dependencies>
<Resources>
<Resource Language="en-us" />
</Resources>
<Applications>
<Application Id="App"
Executable="MyApp.exe"
EntryPoint="$targetentrypoint$">
<uap:VisualElements DisplayName="My App"
Description="My application description"
BackgroundColor="transparent"
Square150x150Logo="Assets\Square150x150Logo.png"
Square44x44Logo="Assets\Square44x44Logo.png">
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png" />
</uap:VisualElements>
</Application>
</Applications>
<Capabilities>
<Capability Name="internetClient" />
<rescap:Capability Name="runFullTrust" />
</Capabilities>
</Package>
All MSIX packages must be signed to install on Windows. The signing certificate's Subject must match the Publisher attribute in the package manifest.
# Create a self-signed certificate for development
$cert = New-SelfSignedCertificate `
-Type Custom `
-Subject "CN=My Company, O=My Company, L=Seattle, S=WA, C=US" `
-KeyUsage DigitalSignature `
-FriendlyName "MyApp Dev Signing" `
-CertStoreLocation "Cert:\CurrentUser\My" `
-TextExtension @("2.5.29.37={text}1.3.6.1.5.5.7.3.3", `
"2.5.29.19={text}")
# Export PFX for CI usage
$password = ConvertTo-SecureString -String "$env:CERT_PASSWORD" -Force -AsPlainText
Export-PfxCertificate -Cert $cert `
-FilePath "MyApp_DevSigning.pfx" `
-Password $password
# Find signtool.exe dynamically (SDK version varies by machine)
$signtool = Get-ChildItem "C:\Program Files (x86)\Windows Kits\10\bin\*\x64\signtool.exe" |
Sort-Object { [version]($_.Directory.Parent.Name) } -Descending |
Select-Object -First 1 -ExpandProperty FullName
# Sign the MSIX package
& $signtool sign /fd SHA256 /a /f "MyApp_DevSigning.pfx" `
/p "$env:CERT_PASSWORD" `
"AppPackages\MyApp_1.0.0.0_x64.msix"
For production distribution outside the Microsoft Store:
Publisher in Package.appxmanifest# Sign with a trusted CA certificate (from certificate store)
& signtool.exe sign /fd SHA256 /sha1 "THUMBPRINT_HERE" `
/tr http://timestamp.digicert.com /td SHA256 `
"AppPackages\MyApp_1.0.0.0_x64.msix"
# Sign with a PFX file
& signtool.exe sign /fd SHA256 /f "production-cert.pfx" `
/p "$env:CERT_PASSWORD" `
/tr http://timestamp.digicert.com /td SHA256 `
"AppPackages\MyApp_1.0.0.0_x64.msix"
Apps submitted to the Microsoft Store are re-signed by Microsoft during ingestion. The development signing certificate is replaced with a Microsoft-issued certificate. No production signing certificate is needed for Store-only distribution.
<!-- For Store submission, use a test certificate during development -->
<PropertyGroup>
<AppxPackageSigningEnabled>true</AppxPackageSigningEnabled>
<PackageCertificateThumbprint>AUTO_GENERATED_BY_VS</PackageCertificateThumbprint>
</PropertyGroup>
| Distribution | Certificate Type | Trusted CA Required |
|---|---|---|
| Development/testing | Self-signed | No (install cert manually) |
| Enterprise sideload | Self-signed or internal CA | No (deploy cert via Group Policy) |
| Direct download | Trusted CA (DigiCert, etc.) | Yes |
| Microsoft Store | Test cert (re-signed by MS) | No |
# Build for Store submission (creates .msixupload)
dotnet publish --configuration Release --runtime win-x64 \
/p:GenerateAppxPackageOnBuild=true \
/p:UapAppxPackageBuildMode=StoreUpload \
/p:AppxBundle=Auto
.msixupload file through Partner CenterApp Installer (.appinstaller) enables direct distribution with auto-update support. Host the files on a web server, network share, or CDN.
<!-- MyApp.appinstaller -->
<?xml version="1.0" encoding="utf-8"?>
<AppInstaller Uri="https://mycompany.com/apps/MyApp.appinstaller"
Version="1.0.0.0"
xmlns="http://schemas.microsoft.com/appx/appinstaller/2021">
<MainPackage Name="MyCompany.MyApp"
Version="1.0.0.0"
Publisher="CN=My Company, O=My Company, L=Seattle, S=WA, C=US"
ProcessorArchitecture="x64"
Uri="https://mycompany.com/apps/MyApp_1.0.0.0_x64.msix" />
<UpdateSettings>
<OnLaunch HoursBetweenUpdateChecks="12"
ShowPrompt="true"
UpdateBlocksActivation="false" />
<AutomaticBackgroundTask />
<ForceUpdateFromAnyVersion>false</ForceUpdateFromAnyVersion>
</UpdateSettings>
</AppInstaller>
Users install via:
ms-appinstaller:?source=https://mycompany.com/apps/MyApp.appinstaller
For managed enterprise environments:
| Method | Tool | Best For |
|---|---|---|
| Microsoft Intune | Intune portal | Cloud-managed devices |
| SCCM/MECM | Configuration Manager | On-premises managed devices |
| Group Policy | DISM / PowerShell | Domain-joined devices |
| PowerShell | Add-AppxPackage | Script-based deployment |
# Enterprise deployment via PowerShell
Add-AppxPackage -Path "\\fileserver\apps\MyApp_1.0.0.0_x64.msix"
# Install for all users (requires admin)
Add-AppxProvisionedPackage -Online `
-PackagePath "MyApp_1.0.0.0_x64.msix" `
-SkipLicense
The App Installer XML file controls automatic update behavior:
<UpdateSettings>
<!-- Check for updates on app launch -->
<OnLaunch HoursBetweenUpdateChecks="12"
ShowPrompt="true"
UpdateBlocksActivation="false" />
<!-- Background update check (Windows 10 1803+) -->
<AutomaticBackgroundTask />
<!-- Allow downgrade (useful for rollback scenarios) -->
<ForceUpdateFromAnyVersion>false</ForceUpdateFromAnyVersion>
</UpdateSettings>
| Setting | Description |
|---|---|
HoursBetweenUpdateChecks | Minimum hours between update checks (0 = every launch) |
ShowPrompt | Show update dialog to user before updating |
UpdateBlocksActivation | Block app launch until update completes |
AutomaticBackgroundTask | Check for updates in background without launching |
ForceUpdateFromAnyVersion | Allow updating from any version (including downgrades) |
For apps that need custom update UI or logic, use Package.Current.CheckUpdateAvailabilityAsync():
using Windows.ApplicationModel;
public class AppUpdateService
{
public async Task<bool> CheckForUpdatesAsync()
{
var result = await Package.Current.CheckUpdateAvailabilityAsync();
return result.Availability == PackageUpdateAvailability.Available
|| result.Availability == PackageUpdateAvailability.Required;
}
}
MSIX supports differential updates -- only changed blocks are downloaded. This is automatic when:
PackageName and Publisher are usedVersion is incrementedNo additional configuration is needed for differential updates.
MSIX bundles (.msixbundle) package multiple architecture-specific MSIX packages into a single downloadable artifact. Windows automatically installs the correct architecture.
# Build for multiple architectures
dotnet publish -c Release -r win-x64 /p:GenerateAppxPackageOnBuild=true
dotnet publish -c Release -r win-arm64 /p:GenerateAppxPackageOnBuild=true
# Find MakeAppx.exe dynamically (SDK version varies by machine)
$makeappx = Get-ChildItem "C:\Program Files (x86)\Windows Kits\10\bin\*\x64\MakeAppx.exe" |
Sort-Object { [version]($_.Directory.Parent.Name) } -Descending |
Select-Object -First 1 -ExpandProperty FullName
# Create bundle
& $makeappx bundle /d "AppPackages" /p "MyApp_1.0.0.0.msixbundle"
<PropertyGroup>
<!-- Auto-generate bundle during build -->
<AppxBundle>Always</AppxBundle>
<AppxBundlePlatforms>x64|arm64</AppxBundlePlatforms>
</PropertyGroup>
MyApp_1.0.0.0.msixbundle
MyApp_1.0.0.0_x64.msix
MyApp_1.0.0.0_arm64.msix
# MSIX-specific build steps (embed in your CI workflow)
# For pipeline structure, see [skill:dotnet-gha-patterns]
jobs:
build-msix:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
- name: Decode signing certificate
run: |
$pfxBytes = [System.Convert]::FromBase64String("${{ secrets.SIGNING_CERT_BASE64 }}")
[System.IO.File]::WriteAllBytes("signing-cert.pfx", $pfxBytes)
shell: pwsh
- name: Build MSIX package
run: |
dotnet publish --configuration Release --runtime win-x64 `
/p:GenerateAppxPackageOnBuild=true `
/p:AppxPackageSigningEnabled=true `
/p:PackageCertificateKeyFile="${{ github.workspace }}\signing-cert.pfx" `
/p:PackageCertificatePassword="${{ secrets.CERT_PASSWORD }}"
shell: pwsh
- name: Upload MSIX artifact
uses: actions/upload-artifact@v4
with:
name: msix-package
path: AppPackages/**/*.msix
- name: Clean up certificate
if: always()
run: Remove-Item -Path "signing-cert.pfx" -ErrorAction SilentlyContinue
shell: pwsh
# MSIX-specific build steps (embed in your ADO pipeline)
# For pipeline structure, see [skill:dotnet-ado-patterns]
steps:
- task: UseDotNet@2
inputs:
packageType: 'sdk'
version: '8.0.x'
- task: DownloadSecureFile@1
name: signingCert
inputs:
secureFile: 'signing-cert.pfx'
- task: DotNetCoreCLI@2
displayName: 'Build MSIX'
inputs:
command: 'publish'
publishWebProjects: false
projects: '**/MyApp.csproj'
arguments: >-
--configuration Release
--runtime win-x64
/p:GenerateAppxPackageOnBuild=true
/p:AppxPackageSigningEnabled=true
/p:PackageCertificateKeyFile=$(signingCert.secureFilePath)
/p:PackageCertificatePassword=$(CERT_PASSWORD)
- task: PublishBuildArtifacts@1
inputs:
pathToPublish: 'AppPackages'
artifactName: 'msix-package'
MSIX packages can contain AOT-compiled binaries for faster startup and smaller runtime footprint. Combine PublishAot with MSIX packaging:
<PropertyGroup>
<PublishAot>true</PublishAot>
<WindowsPackageType>MSIX</WindowsPackageType>
</PropertyGroup>
For AOT MSBuild configuration details (ILLink descriptors, trimming options, platform considerations), see [skill:dotnet-native-aot].
| Feature | Minimum Windows Version | Build Number |
|---|---|---|
| MSIX with Windows App SDK | Windows 10 | Build 19041 (2004) |
| App Installer protocol | Windows 10 | Build 1709 (Fall Creators Update) |
| Auto-update (OnLaunch) | Windows 10 | Build 1709 |
| Background auto-update | Windows 10 | Build 1803 (April 2018 Update) |
| ForceUpdateFromAnyVersion | Windows 10 | Build 1809 |
| MSIX bundle format | Windows 10 | Build 1709 |
| Optional packages | Windows 10 | Build 1709 |
| Modification packages | Windows 10 | Build 1809 |
| App Installer file hosting | Windows 10 | Build 1709 |
| Microsoft Store submission | Windows App SDK 1.6+ | N/A |
<PropertyGroup>
<!-- Minimum supported version (features below this are unavailable) -->
<TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
<!-- Maximum tested version (for adaptive version checks) -->
<TargetPlatformVersion>10.0.22621.0</TargetPlatformVersion>
</PropertyGroup>
The manifest Publisher must exactly match the signing certificate Subject -- mismatches cause SignTool Error: SignerSign() failed at build or sign time.
Self-signed certificates require manual trust installation -- users must install the certificate to Trusted People or Trusted Root Certification Authorities before the MSIX will install.
Never commit PFX files or certificate passwords to source control -- store certificates as CI secrets (GitHub Secrets, Azure DevOps Secure Files) and decode them during the build pipeline.
AppxBundle=Auto produces a bundle only when multiple architectures are built -- for single-architecture builds, it produces a flat .msix file, not a bundle.
MSIX apps run in a container-like sandbox -- file system access is virtualized. Apps writing to AppData get redirected to the package-specific location. Use ApplicationData.Current APIs, not hardcoded paths.
Store submission uses .msixupload not .msix -- set /p:UapAppxPackageBuildMode=StoreUpload to generate the correct upload format.
CI builds on windows-latest include the Windows SDK -- no separate SDK installation step is needed for signtool.exe and MakeAppx.exe.
Do not hardcode TFM paths in CI examples -- use variable references (e.g., ${{ github.workspace }}) so examples work across .NET versions.