Your app works. You package it as MSIX. Now the shell extension is gone, the app can't write to its own install folder, and the uninstaller does nothing.
MSIX breaks working applications in ways the error messages never explain. This module finds out why and fixes it — from PowerShell, without administrator rights.
Install-Module MSIX -Scope CurrentUser
Initialize-MsixToolchain # one-time, downloads + verifies the SDK
$report = Invoke-MsixInvestigation -PackagePath .\app.msix
Invoke-MsixAutoFixFromAnalysis -Report $report # applies what it can prove is safeIt reads the package's Registry.dat via offreg.dll rather than reg.exe load, so it needs no elevation and no privileges to tell you that your COM
shell extension was never going to load.
Got a catalogue rather than one app?
Invoke-MsixBulkAnalysis -Path 'D:\packages' -ReportPath .\fleet.csv
# -> one row per package: Clean / AutoFixable / NeedsManual / Incomplete / ErrorAlso covers: PSF injection with the full TMurgent fixup palette, manifest editing, signing (SignTool / Azure Trusted Signing / Key Vault / in-process SignerSignEx), CI/CD orchestration, sandbox debugging, App Attach VHDX/CIM, Win32 App Isolation, and an 848-test Pester suite.
Security note — this module is designed for use in high-assurance environments (DoD, NATO, financial, healthcare). All downloaded toolchain binaries are Authenticode-verified against a trusted-publisher allowlist before use. Signing secrets are kept in
[SecureString]throughout; they never appear in log output, recommendation text, or on the process command line (with the exception of theSignToolbackend which emits aWrite-Warningwhen this happens).
- Why this module
- Quick start
- Architecture
- Investigation & auto-fix
- PSF injection
- Signing backends
- Standard scripts (PSADT)
- Win32 App Isolation
- Manifest-only fixers
- Pipeline orchestrator
- App Attach (VHDX / CIM)
- Context menus
- Debug & sandbox
- CI/CD
- Tests
- What's new
- Release history
- License
Microsoft's MSIX docs describe a manual workflow for every common conversion
problem: download Procmon, set filters, run the app, eyeball the failures,
hand-edit config.json, copy DLLs, repack, sign. This module collapses that
into a small set of idempotent, pipeline-safe functions.
Key design principles
| Principle | How it's implemented |
|---|---|
| Sign once | Invoke-MsixPipeline unpacks, edits, repacks, then signs a single scratch copy. The original is never overwritten until signing succeeds. |
| No secrets on disk | PfxPassword is [SecureString] everywhere; Get-MsixDebugRecommendation emits Read-Host -AsSecureString placeholders, never the real value. |
| No unverified binaries | Every toolchain download (PSF, Procmon, msixmgr, SDK tools) is Authenticode-verified before use. |
| WhatIf everywhere | All mutating cmdlets support -WhatIf. Pack/unpack still runs so you can preview the result; signing and the final file replacement are skipped. |
| Idempotent | All manifest mutators are safe to run twice — they check before writing. |
References this module automates:
- Package Support Framework overview
- PSF — FileSystem Write Permission
- PSF — Run scripts to create shortcuts
- Accelerators
- Support legacy context menus
- Win32 app isolation
- TMurgent PSF fork
The maintainer owns the
MSIXname on PSGallery (Install-Module MSIX). When developing against a working copy, import by full path so you load the repo version rather than the installed one.
Import-Module 'C:\path\to\MSIX\MSIX.psd1' -Force
# One-time toolchain setup: MakeAppx, signtool, PSF, Procmon, msixmgr
# All from official/signed sources, on demand.
Initialize-MsixToolchainInitialize-MsixToolchain downloads and Authenticode-verifies:
| Component | Source | Used by |
|---|---|---|
MakeAppx.exe + signtool.exe |
Microsoft.Windows.SDK.BuildTools (NuGet) | pack / unpack / sign |
| TMurgent PSF | GitHub releases (Tim Mangan) | Add-MsixPsfV2, Add-MsixStandardScript |
| Sysinternals Process Monitor | Sysinternals (Microsoft) | Invoke-MsixInvestigation -WithProcMon |
| msixmgr | aka.ms/msixmgr (Microsoft) |
New-MsixAppAttachImage |
Skip individual components: Initialize-MsixToolchain -Skip Sdk,Procmon,MsixMgr
MSIX\
├── MSIX.psd1 Module manifest
├── MSIX.psm1 Root module — dot-sources all sub-modules
├── MSIX.Logging.ps1 Write-MsixLog + log-level / file controls
├── MSIX.Core.ps1 Workspace, process runner, tools resolution
├── MSIX.Validation.ps1 Manifest / config / process validators
├── MSIX.Manifest.ps1 XML helpers, namespace registry, pure transforms
├── MSIX.ManifestExtensions.ps1 Manifest-only fixers (desktop6, uap5, uap6…)
├── MSIX.PSF.ps1 Fixup builders + Add-MsixPsfV2 injection engine
├── MSIX.PsfBinaries.ps1 PSF / Procmon / SDK downloader + Authenticode verify
├── MSIX.Signing.ps1 Invoke-MsixSigning (SignTool / TrustedSigning / AzureSignTool)
├── MSIX.Pipeline.ps1 Invoke-MsixPipeline — sign-once orchestrator
├── MSIX.ContextMenu.ps1 com + desktop4/desktop5 context menus (legacy IContextMenu + modern IExplorerCommand)
├── MSIX.Scripts.ps1 PSADT-flavoured standard scripts + Set-MsixScriptSignature
├── MSIX.AppIsolation.ps1 Win32 App Isolation (rescap capabilities, opt-in)
├── MSIX.Investigation.ps1 Static + procmon analysis + recommendations
├── MSIX.Scanners.ps1 Read-only Get-Msix*Candidate/*Entry/HeuristicFinding
├── MSIX.PackageMutators.ps1 Add-MsixCapability + Remove-Msix*Artifact + payload Add-*
├── MSIX.AutoFix.ps1 Invoke-MsixAutoFix + Invoke-MsixAutoFixFromAnalysis
├── MSIX.Detection.ps1 Read-only scanners (fonts, shortcuts, capability hints)
├── MSIX.Debug.ps1 Start-MsixDebugSession + sandbox bootstrap
├── MSIX.AppData.ps1 Container / orphan / merged-fs helpers
├── MSIX.AppAttach.ps1 VHDX/CIM generator (msixmgr, Authenticode-verified)
├── MSIX.Accelerator.ps1 MS Learn accelerator YAML import + apply (safe parser)
├── MSIX.Limitations.ps1 Limitations knowledge base
├── MSIX.Trace.ps1 DebugView / TraceFixup output parser
├── MSIX.MFR.ps1 Modern File Redirection (TMurgent fork)
├── MSIX.VcRuntime.ps1 VC++ runtime detection + bundling
├── MSIX.Compare.ps1 Compare-MsixPackage (manifest + file + signing diff)
├── MSIX.OfflineRegistry.ps1 Offreg (offline hive) read/write helpers
├── MSIX.Distribution.ps1 .appinstaller, modification + framework packages
├── MSIX.Bundle.ps1 .msixbundle: bundle/unbundle + mutate-through-bundle
├── MSIX.RuntimeTest.ps1 Test-MsixDeployment (Hyper-V runtime verification)
├── templates/ .ps1.tmpl files for standard scripts
├── MSIX.Tests/ Pester v5 test suite (700+ tests)
├── docs/ Per-fixup reference, limitations, know-your-installer
├── CONTRIBUTING.md Coding standards and security guidelines
├── EXAMPLES.md 26 copy-paste recipes for all major use cases
└── TEST-PLAN.md 14 manual / integration test scenarios + release checklist
All logging uses Write-Information (stream 6) — fully capturable in CI
pipelines. Security-critical notices use Write-Warning (stream 3) so they
appear in -WarningVariable captures.
$report = Invoke-MsixInvestigation -PackagePath app.msix
$report.Findings | Format-Table Severity, Category, Symptom
$report.SuggestedFixups | ConvertTo-Json -Depth 8 # PSF path
$report.SuggestedManifestFixes | Format-Table # manifest alternatives
$report.RecommendedCommands | Out-File next-steps.ps1Initialize-MsixToolchain
$report = Invoke-MsixInvestigation -PackagePath app.msix -WithProcMon `
-PackageFamilyName 'Contoso.App_8wekyb3d8bbwe' -AppId 'App' `
-DurationSeconds 30 -ProcessName 'app.exe'Invoke-MsixAutoFixFromAnalysis connects investigation to remediation — it
runs the right fixer for every finding and signs once at the end.
$report = Invoke-MsixInvestigation -PackagePath app.msix
Invoke-MsixAutoFixFromAnalysis -Report $report `
-VcRuntimeSourceFolder 'C:\…\VC143.CRT' `
-Pfx cert.pfx -PfxPassword (Read-Host -AsSecureString)
# Preview only — no mutation:
Invoke-MsixAutoFixFromAnalysis -Report $report -DryRun$pw = Read-Host -AsSecureString
# File redirection (writes to install-dir → per-user redirect)
$frf = New-MsixPsfFileRedirectionConfig -Base 'VFS/ProgramFilesX64/App/' `
-Patterns '.*\.log', '.*\.tmp', '.*\.cache'
# Environment variable injection
$env = New-MsixPsfEnvVarConfig -Variables @{ APP_MODE = 'packaged'; APP_VER = '3' }
# Custom working directory
Add-MsixPsfV2 -PackagePath app.msix `
-Fixups @($frf, $env) `
-WorkingDirectory 'VFS/ProgramFilesX64/App/' `
-Pfx cert.pfx -PfxPassword $pwModern File Redirection (MFR — TMurgent fork, ILV-aware):
$rule = New-MsixMfrTraditionalRule -KnownFolder 'ProgramFilesX64' `
-RelativePath 'Contoso/logs' -Patterns '.*\.log' -Cow enablePe
$mfr = New-MsixPsfMfrConfig -TraditionalRules @($rule) -GlobalIlvAware $true
Add-MsixPsfV2 -PackagePath app.msix -Fixups @($mfr) -Pfx cert.pfx -PfxPassword $pwFour backends, all accepting [SecureString] for any secret:
$pw = Read-Host -AsSecureString
# Local signtool + PFX (emits Write-Warning about cmdline exposure)
Invoke-MsixSigning -PackagePath app.msix -Pfx cert.pfx -PfxPassword $pw
# Safe local PFX (SignerSignEx): the password loads the cert in-process and
# signtool signs by thumbprint — the password/PFX path never hit the cmdline.
Invoke-MsixSigning -PackagePath app.msix -Signer SignerSignEx -Pfx cert.pfx -PfxPassword $pw
# Azure Trusted Signing (recommended for production)
Invoke-MsixSigning -PackagePath app.msix -Signer TrustedSigning `
-TrustedSigningAccount 'contoso' -TrustedSigningProfile 'Code Signing' `
-TrustedSigningEndpoint 'https://weu.codesigning.azure.net'
# Azure Key Vault via AzureSignTool
Invoke-MsixSigning -PackagePath app.msix -Signer AzureSignTool `
-KeyVaultUrl 'https://my-vault.vault.azure.net' `
-KeyVaultCertificate 'my-cert' `
-KeyVaultClientId $spId -KeyVaultClientSecret $spSecretGenerate, sign, and inject deployment-toolkit-style PowerShell scripts as PSF
startScript / endScript entries. Customer-specific values are baked in at
generation time.
$pw = Read-Host -AsSecureString
# See bundled templates
Get-MsixStandardScript | Format-Table Name, Description
# Generate + sign (no package touched)
New-MsixStandardScript -Name CreateShortcut `
-Parameters @{ DisplayName = 'Contoso'; Target = 'contoso.exe' } `
-OutputPath C:\src\createshortcut.ps1 `
-Pfx cert.pfx -PfxPassword $pw
# Generate + sign + inject into MSIX in one step
Add-MsixStandardScript -PackagePath app.msix -AppId 'App' `
-Name CreateShortcut `
-Parameters @{ DisplayName = 'Contoso'; Target = 'contoso.exe' } `
-RunOnce -WaitForScriptToFinish `
-Pfx cert.pfx -PfxPassword $pw
# Sign any script with the same cert
Set-MsixScriptSignature -ScriptPath my-script.ps1 -Pfx cert.pfx -PfxPassword $pwBundled templates: CreateShortcut, CopyIconToAppData, CleanupOldUserData,
RegisterFileAssociation, CustomerSettingsBootstrap.
Opt-in AppContainer isolation. The actual isolation switch is the AppContainer
trust level on a partial-trust entry point — Add-MsixAppIsolation sets, on
each <Application>:
EntryPoint="Windows.PartialTrustApplication"(the full-trust entry point hard-requiresrunFullTrust, which keeps the process full-trust — so it could never isolate);TrustLevel="appContainer"; and- removes
runFullTrust(incompatible with AppContainer; the partial-trust entry point doesn't need it).
Two modes:
-Mode |
Manifest | Status | Behavior |
|---|---|---|---|
| AppContainer (default) | uap10:RuntimeBehavior="packagedClassicApp" |
GA (Win10 2004+) | ungranted access denied |
| AppSilo | uap18:RuntimeBehavior="appSilo" + uap18:EntryPoint="Isolated.App" + isolatedWin32-* |
preview (Win11 24H2) | access brokered (consent prompts); raises MinVersion to 26100 |
-Capabilities are standard package capabilities in AppContainer mode (default:
none — strictest) and isolatedWin32-* / device capabilities in AppSilo mode
(default: isolatedWin32-promptForAccess).
$pw = Read-Host -AsSecureString
Get-MsixIsolationCapability # rich objects: Name / ElementType / Description (AppSilo caps)
# GA AppContainer (strict): app runs in an AppContainer, ungranted access denied.
Add-MsixAppIsolation -PackagePath app.msix -Pfx cert.pfx -PfxPassword $pw
# ... grant specific access:
Add-MsixAppIsolation -PackagePath app.msix -Capabilities internetClient -Pfx cert.pfx -PfxPassword $pw
# Win32 App Isolation silo (preview) with the file-access broker:
Add-MsixAppIsolation -PackagePath app.msix -Mode AppSilo -Pfx cert.pfx -PfxPassword $pw
Remove-MsixAppIsolation -PackagePath app.msix -Pfx cert.pfx -PfxPassword $pw # restores full trustTwo things that can't be isolated (the cmdlet warns/throws) — with escape hatches:
- PSF packages (
PsfLauncher*.exeentry point) — PSF injects fixup DLLs into the target process, which AppContainer blocks. RunRemove-MsixPsffirst: it restores the real executable from config.json and strips the PSF payload (warning about every behaviour that disappears with it). windows.comServerextensions (e.g. a COM shell context-menu like NppShell) — invalid with a partial-trust entry point. Pass-RemoveComServerto strip the COM server and its Explorer context-menu verbs (that functionality is lost).
Verify and tune with the isolation toolkit:
# Would this package isolate? (static manifest verdict with reasons)
Test-MsixIsolation -PackagePath app-isolated.msix
# Is the RUNNING app actually in an AppContainer? (token check: S-1-15-2 SID)
Test-MsixIsolation -PackageFamilyName 'MyApp_abc123def' # or -ProcessId <pid>
# Which capabilities does the app need? Trace it, then map the denials:
$pml = Invoke-MsixProcMonCapture -ScriptBlock { <# drive the app #> }
Get-MsixProcMonFailure -PmlPath $pml | Get-MsixIsolationAdvice -Mode AppSiloValidate first. Many legacy apps break under isolation because they expect broad filesystem/registry access. Prompts alone are NOT proof of isolation (ASR rules also prompt) — only the
S-1-15-2AppContainer SID is (Test-MsixIsolation). The AppSilo layer is a preview feature; if its brokering doesn't engage on a given build the app still runs in the AppContainer (access denied rather than prompted). SeeTEST-PLAN.mdScenario 6.
Invoke-MsixPipeline supports AppIsolation as a first-class config key —
applied in the same unpack/repack/sign pass as publisher updates and PSF
injection.
Alternatives to PSF for symptoms the modern manifest schema handles natively (faster, no foreign DLLs, survives Windows updates cleanly):
| Cmdlet | Schema | Min OS | Replaces |
|---|---|---|---|
Set-MsixFileSystemWriteVirtualization |
desktop6 | 19041 | PSF FileRedirection (broad) |
Set-MsixRegistryWriteVirtualization |
desktop6 | 19041 | RegLegacy Hklm2Hkcu (broad) |
Set-MsixInstalledLocationVirtualization |
uap10 | 19041 | FRF + update-time policy |
Add-MsixLoaderSearchPathOverride |
uap6 | 17134 | DynamicLibraryFixup |
Add-MsixFirewallRule |
desktop2 | 15063 | post-install netsh |
Add-MsixStartupTask |
uap5 | 15063 | HKLM/HKCU \Run keys |
Add-MsixProtocolHandler |
uap | any | host-side protocol script |
Add-MsixFileTypeAssociation |
uap | any | host-side FTA script (now with -Logo/-InfoTip/EditFlags/-Verbs) |
Add-MsixService |
desktop6 | 19041 | services the capture's installer registered (agents/updaters/licensing) |
Add-MsixShellHandlerExtension |
desktop2 | 15063 | preview/property/thumbnail shell handlers from the registry |
Add-MsixToastActivator |
desktop + com | 16299 | toast click re-activation |
Add-MsixPackageDependency |
foundation | any | bundling framework DLLs (VCLibs, WindowsAppRuntime as <PackageDependency>) |
Set-MsixMutablePackageDirectory |
desktop6 | 18362 | plugin/mod folders via %ProgramFiles%\ModifiableWindowsApps |
Add-MsixPackageCertificate |
foundation | any | the capture's certutil step (windows.certificates, per-package store) |
Add-MsixAppExtensionHost / Add-MsixAppExtension |
uap3 | 14393 | ad-hoc plugin folder drops (AppExtensionCatalog contract) |
Add-MsixAutoPlayHandler |
uap | any | AutoPlay registry handlers |
Add-MsixShareTarget |
uap | any | Share-sheet integration |
Add-MsixFullTrustProcess |
desktop | 14393 | UWP-main + Win32-companion pattern |
Scanners feed these into Invoke-MsixInvestigation findings and
Invoke-MsixAutoFixFromAnalysis planning: Get-MsixServiceEntry,
Get-MsixShellHandlerEntry, Get-MsixPackageCertificateCandidate
(certificate declaration is opt-in via -DeclarePackageCertificates +
-PackageCertificateStore — installing a certificate is a trust decision).
Distribution (after post-processing): New-MsixAppInstallerFile generates
a .appinstaller with the full auto-update policy; New-MsixModificationPackage
builds a uap4:MainPackageDependency customization package that layers
settings/plugins onto a vendor MSIX without touching it.
Multi-arch bundles: New-MsixBundle / Expand-MsixBundle /
Get-MsixBundleInfo wrap MakeAppx bundle operations, and
Invoke-MsixBundleOperation applies any mutator to each inner package
(unbundle → mutate → rebundle → sign) so the whole toolkit works on
.msixbundle inputs, not just single .msix files.
Localization repair: Update-MsixResourcePri regenerates resources.pri
via makepri, and Set-MsixBrandMetadata -RegeneratePri makes brand edits on
ms-resource:-localized packages actually take effect at runtime.
Shared runtime frameworks: stop bundling a JRE into every app.
New-MsixFrameworkPackage packages a runtime once as a <Framework> package;
Add-MsixRuntimeDependency wires each consumer (<PackageDependency> +
optional JAVA_HOME/DOTNET_ROOT env wiring). Get-MsixBundledRuntime
detects private runtime copies in captures, and autofix can strip + rewire
them (opt-in, explicit framework identity).
Modification packages: New-MsixModificationPackage layers files AND
settings (-RegistryContent builds Registry.dat/User.dat) onto a vendor
MSIX without touching it; ConvertTo-MsixModificationPackage productizes the
delta between a vendor package and a customized copy automatically.
Static analysis says whether a package should work; Test-MsixDeployment
proves whether it does. It installs a signed package into a clean Hyper-V VM
over PowerShell Direct (no VM networking), launches it via shell:AppsFolder,
and probes liveness — returning a verdict object shaped like the other
Test-Msix* results, with the event-log artifacts wired to feed the
analyze → autofix loop.
$cred = Get-Credential
Test-MsixDeployment -PackagePath app.msix -VMName 'Win11-24H2' `
-Credential $cred -CertPath app.cer -Checkpoint 'clean'
# -> Passed / Reasons / ProcessAlive / CrashDetected / EventLogArtifactsInvoke-MsixPipeline applies every requested stage to a single workspace,
then signs once at the very end. Atomic: if signing fails, the original
target is never overwritten — the unsigned scratch package is preserved at
UnsignedOutputPath for manual re-sign.
$pw = Read-Host -AsSecureString
Invoke-MsixPipeline -PackagePath app.msix -OutputPath app-fixed.msix -Config @{
Publisher = 'CN=Contoso, O=Contoso, C=NL'
PSF = @{
Fixups = @( New-MsixPsfFileRedirectionConfig -Base 'logs' -Patterns '.*\.log' )
WorkingDirectory = 'VFS/ProgramFilesX64/Contoso/'
}
AppIsolation = @{
Capabilities = 'isolatedWin32-promptForAccess', 'isolatedWin32-userProfileMinimal'
}
Signing = @{
Pfx = 'cert.pfx'
PfxPassword = $pw
UnsignedOutputPath = 'C:\drop\app-unsigned-fallback.msix'
}
}# VHDX — auto-sized, NTFS, optional ACLs
New-MsixAppAttachImage -PackagePath app.msix -OutputPath app.vhdx
# CIM — no Hyper-V required
New-MsixAppAttachImage -PackagePath app.msix -OutputPath app.cim -FileType cim
# Multiple packages into one image
New-MsixAppAttachImage -PackagePath @('a.msix','b.msix') -OutputPath bundle.vhdx -SizeGB 4
Test-MsixAppAttachImage -ImagePath app.vhdx# Legacy IContextMenu COM shell extension (com + desktop4/desktop5, Win10 1809+ / build 17763+).
# Emits the com:SurrogateServer registration AND the desktop4:FileExplorerContextMenus
# verb in one call — the field-verified pattern Explorer actually activates.
Add-MsixLegacyContextMenu -PackagePath app.msix `
-ShellExtDll 'VFS\ProgramFilesX64\App\ShellExt.dll' `
-Clsid 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' `
-DisplayName 'My Context Menu' `
-FileTypes '*', '.log', 'Directory' `
-Pfx cert.pfx -PfxPassword (Read-Host -AsSecureString)
# Modern IExplorerCommand (desktop4, recommended for new extensions)
Add-MsixFileExplorerContextMenu -PackagePath app.msix `
-AppId 'App' -VerbId 'open' -VerbClsid '{XXXXXXXX-…}' `
-FileTypes '.txt', '.log'
# Alternative classic-handler schema (-Schema desktop9|Both): the MS-documented /
# commercial-tool shape (windows.fileExplorerClassicContextMenuHandler).
# Win11 21H2+ only; appears in the CLASSIC menu ("Show more options").
Add-MsixLegacyContextMenu -PackagePath app.msix -Schema Both `
-ShellExtDll 'VFS\ProgramFilesX64\App\ShellExt.dll' `
-Clsid 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' -DisplayName 'My Context Menu'# Spin up Windows Sandbox with the broken package + this module pre-loaded
$cfg = New-MsixSandboxConfig -DropFolder 'C:\drop' -PackageName 'broken.msix'
Start-MsixSandbox -ConfigPath $cfg
# Inside the sandbox the LogonCommand auto-runs Start-MsixDebugSession which:
# - Runs Invoke-MsixInvestigation (static)
# - Prints numbered copy-paste-ready fix commands (saved to recommended-commands.ps1)
# - Optionally installs the package, opens Procmon + DebugViewAll functions are non-interactive — no Read-Host, no PromptForChoice.
Import-Module .\MSIX\MSIX.psd1 -Force
Initialize-MsixToolchain | Out-Null
$report = Invoke-MsixInvestigation -PackagePath $env:BUILD_OUTPUT
if ($report.Findings | Where-Object Severity -eq 'Error') {
throw 'Compatibility errors detected — aborting.'
}
Invoke-MsixPipeline -PackagePath $env:BUILD_OUTPUT -OutputPath $env:BUILD_FIXED `
-Config @{
PSF = @{ Fixups = $report.SuggestedFixups }
Signing = @{
Signer = 'TrustedSigning'
TrustedSigningAccount = $env:TS_ACCOUNT
TrustedSigningProfile = $env:TS_PROFILE
TrustedSigningEndpoint = $env:TS_ENDPOINT
}
}Pester v5 tests under MSIX.Tests\. 500+ tests cover pure functions, manifest
transforms, XML security (XXE / billion-laughs rejection), secret non-leakage,
input validation, idempotency, and the module export contract — plus
Integration-tagged tests that pack real .msix fixtures via MakeAppx (skipped
when the SDK toolchain isn't present). A coverage-map guardrail asserts every
mutating cmdlet is exercised by a test.
Install-Module Pester -MinimumVersion 5.5 -Scope CurrentUser
# All tests
.\MSIX.Tests\Invoke-MsixTests.ps1
# By tag
.\MSIX.Tests\Invoke-MsixTests.ps1 -Tag Security
.\MSIX.Tests\Invoke-MsixTests.ps1 -Tag Manifest,BuildersCI runs PSScriptAnalyzer (Error + Warning) and Pester on every push / PR via
.github/workflows/ci.yml. Releases publish to PSGallery via
.github/workflows/publish.yml.
Bulk / fleet analysis — Invoke-MsixBulkAnalysis runs the whole
investigate → plan pipeline over a folder of packages and emits one comparable
row each (Clean / AutoFixable / NeedsManual / Incomplete / Error).
Built for catalogues: every package is isolated so a corrupt one cannot end the
run, results stream to the CSV as they complete, and -Resume skips what is
already recorded — so a multi-day corpus run can be stopped and restarted at
will. The AutoFixable verdict is decided by asking the real planner what it
would do, not by a second copy of the fixable-category list.
Hardening release from a full-codebase audit — data-loss fixes (Remove-MsixPsf
deleting an app's own config.json; Add-MsixVcRuntimeBundle signing over its
input; -WhatIf broken across every mutator), signing-toolchain security (SxS
DLL verification, PFX private-key residue, the password reaching the log file),
and Windows PowerShell 5.1 compatibility. See
CHANGELOG.md.
Shared runtime frameworks (#130) — package a JRE/.NET/Python runtime ONCE
as a <Framework> package (New-MsixFrameworkPackage) and wire any number of
apps to it (Add-MsixRuntimeDependency: dependency + optional
JAVA_HOME/DOTNET_ROOT env wiring). Get-MsixBundledRuntime finds private
runtime copies in captures; autofix can strip + rewire them (opt-in, explicit
framework identity).
Modification packages completed (#131) — New-MsixModificationPackage -RegistryContent layers HKLM/HKCU settings (built into
Registry.dat/User.dat), and ConvertTo-MsixModificationPackage
productizes the delta between a vendor package and a customized copy.
Test-MsixDeployment -ModificationPackagePaths probes main + mods together.
From 0.72 (source-only, first published here):
.msixbundlesupport —New-MsixBundle/Expand-MsixBundle/Get-MsixBundleInfo, andInvoke-MsixBundleOperationto run any mutator against each inner package (unbundle → mutate → rebundle → sign, atomic).- resources.pri regeneration —
Update-MsixResourcePri+Set-MsixBrandMetadata -RegeneratePrimake brand edits onms-resource:-localized packages actually take effect. - Runtime deployment testing —
Test-MsixDeploymentdeploys into a clean Hyper-V VM over PowerShell Direct, launches, probes, and returns a verdict whose artifacts feed the analyze → autofix loop. - In-process signing —
Invoke-MsixSigning -Signer SignerSignExsigns by thumbprint with the certificate loaded in-process: no PFX password or path on any command line.
Earlier in the 0.71 line: the App Isolation rework (partial-trust
AppContainer/AppSilo that provably isolates), the isolation toolkit
(Remove-MsixPsf, Test-MsixIsolation, Get-MsixIsolationAdvice), packaged
services, shell handlers, toast activators, package dependencies/certificates,
distribution (.appinstaller), and the module-wide Get-Help repair.
The full per-version history lives in CHANGELOG.md.
This module is free to use, modify, and build on — including commercially, inside your own organisation, and as part of your own packaging workflows. It is licensed under the PolyForm Shield 1.0.0 license.
In plain English: do whatever you like with it, with one exception — you may not use it to build a product that competes with this module (for example, folding it into a rival MSIX-packaging tool or service). If that's what you're after, get in touch and we'll talk about a separate commercial license.
PolyForm Shield is a source-available license, not an OSI open-source license — that single competitive carve-out is the only difference from a permissive license like Apache-2.0. Contributions are welcome under the same terms (see CONTRIBUTING.md).
Third-party binaries: PSF binaries fetched at runtime remain the property of their respective owners (Microsoft / Tim Mangan / TMurgent Technologies). This module does not redistribute any binaries — it downloads them on demand from the authors' official release channels and verifies Authenticode signatures before use.