What Even Is an MSIX Package?
You know how when you install an app from the Microsoft Store, it just... works? No "Next → Next → Finish" wizard. That's MSIX — Microsoft's modern app packaging format.
Think of it like a zip file that contains three things:
- The app's binaries (like
pwsh.exe) - A manifest (
AppxManifest.xml) that tells Windows: "My name is X, my publisher is Y, I need these capabilities" - Digital signatures proving it's legit
Every MSIX has a Package Family Name (PFN). It's the identity other apps use to find your app on the system. It looks like this:
The format is {IdentityName}_{PublisherIdHash}.
The 8wekyb3d8bbwe part is a hash of Microsoft's publisher certificate — same for all
Microsoft apps. The first part comes from the <Identity Name="..."> field
in the manifest.
Think of the PFN as a phone number in a directory. Other apps look you up by this number. Change it, and nobody can find you.
How Windows Terminal Finds PowerShell
When you open Windows Terminal, it auto-detects installed PowerShell versions and creates profiles for them. But here's the thing — it does this with hardcoded phone numbers.
microsoft/terminal — PowershellCoreProfileGenerator.cpp
static constexpr std::wstring_view POWERSHELL_PFN{
L"Microsoft.PowerShell_8wekyb3d8bbwe"
};
static constexpr std::wstring_view POWERSHELL_PREVIEW_PFN{
L"Microsoft.PowerShellPreview_8wekyb3d8bbwe"
};
Terminal searches for apps with exactly these two PFNs. If it finds one, it creates a profile
(that's the entry you see in your Terminal dropdown with source: "Windows.Terminal.PowershellCore").
If it doesn't find a match — no profile.
starts up"] --> Q{"Check PFN
directory"} Q -->|"Microsoft.PowerShell
_8wekyb3d8bbwe"| Y["Profile created"] Q -->|"Microsoft.PowerShellPreview
_8wekyb3d8bbwe"| Y Q -->|"Any other PFN"| N["Not found"] style T fill:#6e8efb22,stroke:#6e8efb,color:#e2e2ea style Q fill:#1c1c26,stroke:#2a2a3a,color:#e2e2ea style Y fill:#4ade8018,stroke:#4ade80,color:#4ade80 style N fill:#f8717118,stroke:#f87171,color:#f87171
Terminal has exactly two contacts saved: "PowerShell" and "PowerShell Preview." It calls those numbers every time it starts. If nobody picks up at those numbers, Terminal assumes PowerShell isn't installed.
What Broke in 7.6.0
PowerShell 7.6.0 was special. It was the first release that's both the current Stable release AND an LTS (Long-Term Support) release simultaneously. Previous LTS releases (7.2, 7.4) were always separate from the "latest" track.
The build pipeline reads a config file called tools/metadata.json:
tools/metadata.json (at build time)
{
"LTSRelease": { "Package": true }, // ← build LTS MSIX
"StableRelease": { "Package": true } // ← build Stable MSIX
}
When LTSRelease.Package is true, the pipeline passes
-LTS to the MSIX builder function. That function has this logic:
tools/packaging/packaging.psm1 — New-MSIXPackage
if ($LTS) {
$ProductName += '-LTS' # "PowerShell" → "PowerShell-LTS"
}
This changes the identity baked into the manifest:
<Identity Name="Microsoft.PowerShell-LTS" ... />
Which produces the PFN:
Terminal doesn't have this number saved. It calls
Microsoft.PowerShell_8wekyb3d8bbwe — nobody answers.
PowerShell disappears from Terminal.
It's exactly like if your phone company changed your number from 555-1234 to 555-1234-LTS and didn't tell anyone who had your old number saved.
LTSRelease.Package = true"] --> F["New-MSIXPackage -LTS"] F --> ID["Identity Name:
Microsoft.PowerShell-LTS"] end subgraph INSTALL ["Installed Package"] ID --> PFN["PFN: Microsoft.PowerShell-LTS_8wekyb3d8bbwe"] end subgraph TERMINAL ["Windows Terminal"] T1["Looking for:
Microsoft.PowerShell_8wekyb3d8bbwe"] T2["Looking for:
Microsoft.PowerShellPreview_8wekyb3d8bbwe"] end PFN -.- |"does NOT match"| T1 PFN -.- |"does NOT match"| T2 style BUILD fill:#1c1c26,stroke:#2a2a3a,color:#e2e2ea style INSTALL fill:#f8717118,stroke:#f87171,color:#e2e2ea style TERMINAL fill:#1c1c26,stroke:#2a2a3a,color:#e2e2ea style M fill:#22222e,stroke:#2a2a3a,color:#e2e2ea style F fill:#22222e,stroke:#2a2a3a,color:#e2e2ea style ID fill:#22222e,stroke:#f87171,color:#f87171 style PFN fill:#f8717133,stroke:#f87171,color:#f87171 style T1 fill:#22222e,stroke:#4ade80,color:#4ade80 style T2 fill:#22222e,stroke:#4ade80,color:#4ade80
The Fix (PR #27056)
The fix was elegant: build TWO MSIX packages when a release is both LTS and Stable.
Before (7.6.0 release day)
One call to New-MSIXPackage -LTS
Produces:
PowerShell-LTS-7.6.0.msix
Identity: Microsoft.PowerShell-LTS
Terminal: "Who?"
After (the fix)
Two calls to New-MSIXPackage
Produces:
PowerShell-7.6.0.msix (stable)
PowerShell-LTS-7.6.0.msix (lts)
Terminal: "Found you!"
The pipeline logic now forks when both flags are true:
.pipelines/templates/packaging/windows/package.yml
$LTS = $metadata.LTSRelease.Package
$Stable = $metadata.StableRelease.Package
# Build LTS package (with -LTS identity)
Start-PSPackage -Type msix -LTS:$LTS
# NEW: When both LTS + Stable, build a second MSIX with stable identity
if ($LTS -and $Stable) {
Start-PSPackage -Type msix # no -LTS → "Microsoft.PowerShell"
}
These get bundled into two separate .msixbundle files:
Now users who install the Stable bundle get the PFN that Terminal knows. Users who install the LTS bundle get a separate Store listing — but Terminal won't auto-detect it because it still only knows two PFNs.
LTS: true, Stable: true"] --> PKG["package.yml"] PKG --> C1["Call 1: New-MSIXPackage -LTS"] PKG --> C2["Call 2: New-MSIXPackage (no -LTS)"] C1 --> M1["PowerShell-LTS-7.6.0-win-x64.msix"] C1 --> M2["PowerShell-LTS-7.6.0-win-arm64.msix"] C2 --> M3["PowerShell-7.6.0-win-x64.msix"] C2 --> M4["PowerShell-7.6.0-win-arm64.msix"] M1 & M2 --> B1["PowerShell-LTS-7.6.0.msixbundle"] M3 & M4 --> B2["PowerShell-7.6.0.msixbundle"] B1 --> STORE1["Store: LTS listing"] B2 --> STORE2["Store: Stable listing"] B2 --> TERM["Terminal finds it"] style META fill:#6e8efb22,stroke:#6e8efb,color:#e2e2ea style PKG fill:#22222e,stroke:#2a2a3a,color:#e2e2ea style C1 fill:#22222e,stroke:#fb923c,color:#fb923c style C2 fill:#22222e,stroke:#4ade80,color:#4ade80 style M1 fill:#1c1c26,stroke:#2a2a3a,color:#8888a0 style M2 fill:#1c1c26,stroke:#2a2a3a,color:#8888a0 style M3 fill:#1c1c26,stroke:#2a2a3a,color:#8888a0 style M4 fill:#1c1c26,stroke:#2a2a3a,color:#8888a0 style B1 fill:#fb923c18,stroke:#fb923c,color:#fb923c style B2 fill:#4ade8018,stroke:#4ade80,color:#4ade80 style STORE1 fill:#1c1c26,stroke:#2a2a3a,color:#8888a0 style STORE2 fill:#1c1c26,stroke:#2a2a3a,color:#8888a0 style TERM fill:#4ade8018,stroke:#4ade80,color:#4ade80
The Remaining Gaps (What no-identd Is Saying)
The original symptom — Terminal can't find PowerShell — was fixed by PR #27056. But
no-identd looked deeper and found four more problems that are all
consequences of the LTS/Stable split being rushed out the door.
The LTS Download Link Is Stale
aka.ms/powershell-release?tag=lts still points to v7.4.14
(the previous LTS). Someone forgot to update the redirect config for 7.6.0.
No LTS MSI Installer
Enterprises need MSI for Group Policy / SCCM / Intune deployments. LTS packages exist for Linux & macOS but no LTS-branded MSI for Windows.
Medium ImpactWindows Update Catalog Issues
PowerShell via Windows Update / WSUS has a long history of breaking. 7.2 LTS stopped appearing after v7.2.19. 7.4 LTS took months. Now 7.6.0 might be stuck too.
Medium Impactx86 Builds Silently Dropped
Hashes file referenced x86 artifacts, but actual binaries were missing. Resolution: hashes removed instead of binaries added. No deprecation notice issued.
Lower ImpactAnd Then There's the Terminal PFN Gap
Even after the fix, there's a downstream problem: users who intentionally install the LTS-identity package still won't get a Terminal profile, because Terminal only knows two PFNs.
The fix would be adding a third PFN constant to Terminal's source:
PowershellCoreProfileGenerator.cpp (proposed addition)
static constexpr std::wstring_view POWERSHELL_PFN{
L"Microsoft.PowerShell_8wekyb3d8bbwe"
};
static constexpr std::wstring_view POWERSHELL_PREVIEW_PFN{
L"Microsoft.PowerShellPreview_8wekyb3d8bbwe"
};
static constexpr std::wstring_view POWERSHELL_LTS_PFN{
L"Microsoft.PowerShell-LTS_8wekyb3d8bbwe"
};
This is what no-identd asked you to do: File an issue on
microsoft/terminal requesting they add the LTS PFN to their discovery logic.
It's a clear, well-scoped fix — about 3 lines of C++ — and it gets your name
visible in the Terminal repo, not just PowerShell.
Timeline of Events
v7.6.0 released. Pipeline built only LTS-identity MSIX. Terminal profiles break for Store/MSIX users.
PR #27056 merged. Dual MSIX build infrastructure added. Backported same day via #27069 and #27071.
PR #27077 merged. PhoneProductId corrected for LTS Store listing. Updated release assets now include both bundles.
Issue #27064 filed. User reports broken Terminal profile. Root cause already fixed but release assets not yet widespread.
no-identd's comment. Consolidates remaining issues: stale aka.ms redirect, missing LTS MSI, x86 dropped silently, no docs about the MSIX split.
Issue #27064 closed. Original symptom (Terminal profile) resolved. Remaining gaps tracked as separate concerns.
Your Move
no-identd mentioned you because your earlier analysis on #27064 traced
the root cause chain accurately. They're asking you to file an issue on
microsoft/terminal requesting Terminal add the LTS PFN to its
PowerShell discovery logic.
Why This Is a Great Opportunity
- Clear scope — Add ~3 lines to
PowershellCoreProfileGenerator.cppand the corresponding lookup function - You already have the analysis — Your #27064 comment traced the entire chain from
metadata.jsonthrough manifest generation to Terminal's PFN lookup - Cross-repo visibility — Gets your name in the Terminal repo, not just PowerShell. Different team, broader exposure.
- Natural follow-through — You identified the root cause in PowerShell, now you're helping the downstream consumer (Terminal) adapt. That's what serious contributors do.
LTS + Stable"] --> B["Build pipeline
creates 2 MSIXs"] B --> C["Stable PFN"] B --> D["LTS PFN"] C --> E["Terminal knows"] D --> F["Terminal doesn't
know"] F --> G["YOUR ISSUE:
Add LTS PFN
to Terminal"] style A fill:#4ade8018,stroke:#4ade80,color:#4ade80 style B fill:#4ade8018,stroke:#4ade80,color:#4ade80 style C fill:#4ade8018,stroke:#4ade80,color:#4ade80 style D fill:#fb923c18,stroke:#fb923c,color:#fb923c style E fill:#4ade8018,stroke:#4ade80,color:#4ade80 style F fill:#f8717118,stroke:#f87171,color:#f87171 style G fill:#fbbf2418,stroke:#fbbf24,color:#fbbf24