Files
Allen Houchins 4d55e96f9f Add Microsoft Store apps in Windows self-service article and guide (#50637)
**Related issue:** NA

Adds an article and companion guide on putting **Microsoft Store apps**
into Windows self-service using winget and `.ps1` script-only packages.

| File | Category | URL |
|---|---|---|
|
`build-your-own-windows-self-service-with-winget-and-script-only-packages.md`
| `articles` |
`/articles/build-your-own-windows-self-service-with-winget-and-script-only-packages`
|
|
`build-your-own-windows-self-service-with-winget-and-script-only-packages-guide.md`
| `guides` |
`/guides/build-your-own-windows-self-service-with-winget-and-script-only-packages-guide`
|

## What changed and why

Fleet 4.89.0 added an uninstall script, pre-install query, and
post-install script to script-only packages. On Linux that was enough to
build a [self-service catalog on apt and
dnf](https://fleetdm.com/articles/build-your-own-linux-self-service-with-script-only-packages).
These are the Windows counterparts, scoped specifically to Store apps.

The per-app work is two lines:

```powershell
winget install --id <StoreId> --source msstore --accept-package-agreements --accept-source-agreements --disable-interactivity
winget uninstall --id <StoreId> --accept-source-agreements --disable-interactivity
```

Everything else in each script is boilerplate, and the generator emits
it.

## The constraint both pieces are built around

Fleet's agent runs Windows scripts as SYSTEM
(`orbit/pkg/scripts/exec_windows.go:17`), and Store apps cannot be
installed that way:

- The `msstore` source rejects device-wide installs outright: "Device
wide install for msstore type is not supported under admin context"
([winget-cli#3553](https://github.com/microsoft/winget-cli/issues/3553)).
Store packages are per-user by design.
- winget's CLI is [not supported in the system
context](https://learn.microsoft.com/en-us/windows/package-manager/winget/troubleshooting)
at all, because App Installer is an MSIX package that cannot be
registered for `NT AUTHORITY\SYSTEM`.
- Running winget in the system context is still [an open feature
request](https://github.com/microsoft/winget-pkgs/issues/346975).

So the scripts run winget inside the logged-on user's session via a
short-lived scheduled task, mirroring the pattern already used by
Fleet's own per-user Windows maintained apps
(`ee/maintained-apps/inputs/winget/scripts/figma_install.ps1`).

## Two things worth a reviewer's attention

Both are corrections that fall out of the Store focus, and both would
have produced silently wrong content:

1. **Verification uses `Get-AppxPackage -AllUsers`, not the registry.**
Store apps never register in the HKLM uninstall keys, so a registry
check fails on a perfectly good install. This is called out explicitly
in the guide's Troubleshoot section, since it's a natural wrong instinct
if you've built tiles for ordinary Windows installers.
2. **`winget install --scope machine` is documented as a trap, not a
shortcut.** For a Store package it [installs under the SYSTEM
account](https://github.com/microsoft/winget-cli/issues/4748) instead of
provisioning the app, which reports success and leaves users with
nothing.

The scheduled task's exit code also doesn't propagate back to the
calling script, so the install reports success whenever the task ran.
Both pieces treat the post-install verification as mandatory rather than
optional because of this.

## Machine-wide path

For apps that must exist for every user, the content documents `winget
download` plus `Add-AppxProvisionedPackage`, which does work as SYSTEM,
along with its two costs: license download [requires Entra ID
authentication](https://learn.microsoft.com/en-us/windows/package-manager/winget/download)
by a Global Administrator, User Administrator, or License Administrator,
and you now have a file to host, so it wants a Fleet custom package
rather than a script-only one.

## Notes for reviewers

- **Content only.** No Go, frontend, migration, or config changes, so no
changes file is needed and the code-focused template sections below are
removed as the template instructs.
- **The PowerShell has not been executed.** There is no `pwsh` on the
authoring machine. The generator's here-string escaping was traced by
hand but not run. Worth one execution on a real Windows host before
publish.
- **`Microsoft.CompanyPortal` / `9WZDNCRFJ3PZ`** are used as the worked
example. Both identifiers are now verified (PackageFamilyName
`Microsoft.CompanyPortal_8wekyb3d8bbwe`), and the guide still tells
readers to derive both themselves.
- **A verification pass was run over every claim** (Fleet docs,
Microsoft Learn, winget-cli issues). It caught one real bug: `winget
uninstall` with no flags can hang on an msstore source-agreement prompt
([winget-cli#1736](https://github.com/microsoft/winget-cli/issues/1736)),
invisible inside the scheduled task. All uninstalls now carry
`--accept-source-agreements`, both directions carry
`--disable-interactivity`, and the explorer.exe owner lookup takes the
first result so multiple explorer processes can't break
`Register-ScheduledTask`.
- **`articleImageUrl` is intentionally absent** from the article. The
build treats it as optional, but the Linux article has one, so a
`1200x627@2x.png` in `website/assets/images/articles/` plus the meta tag
would bring it to parity. The guide has none, matching the Linux guide.
- **`publishedOn` is `2026-08-05`** on both. Update if these are being
scheduled.

Checked against the build's enforced constraints
(`website/scripts/build-static-content.js`): valid `category`,
`articleTitle` matches each H1 exactly, descriptions are 133 and 125
characters (limit 150), `publishedOn` matches the required ISO pattern,
no `@fleetdm.com` addresses. `check-pr-template` does not run on this
PR, since it only triggers on `frontend/**`, `**/*.go`, `go.mod`, and
`go.sum`.

# Checklist for submitter

## Testing

- [ ] QA'd all new/changed functionality manually

Not applicable to content-only changes. Verified instead by running the
website build's own validation rules against both files, and by sourcing
every technical claim to Microsoft Learn, `winget-cli` issues, or
Fleet's docs and code. The PowerShell samples are unexecuted, as noted
above.
2026-08-07 12:19:48 -05:00
..
2026-06-19 17:47:50 -07:00
2026-06-19 17:47:50 -07:00
2026-06-19 17:47:50 -07:00
2026-07-30 11:03:23 +09:00
2026-08-05 20:38:39 -05:00