Add Windows instructions for FMAs (#34671)

Related: #34069

---------

Co-authored-by: George Karr <georgekarrv@users.noreply.github.com>
Co-authored-by: Jonathan Katz <44128041+jkatz01@users.noreply.github.com>
This commit is contained in:
Carlo
2025-10-24 08:09:51 -04:00
committed by GitHub
co-authored by George Karr Jonathan Katz
parent d84e75d7ff
commit ba4964d87c
+133 -1
View File
@@ -5,7 +5,7 @@
1. Create a new issue using the [New Fleet-maintained app](https://github.com/fleetdm/fleet/issues/new?template=fma-request.md) issue template
2. Find the app's metadata in its [Homebrew formulae](https://formulae.brew.sh/)
3. Create a new mainfiest file called `$YOUR_APP_NAME.json` in the `inputs/homebrew/` directory. For
example, if you wanted to add Box Drive, create the file `inputs/homebrew/box-drive.json`.
example, if you wanted to add Box Drive, create the file `inputs/homebrew/box-drive.json`.
4. Fill out the file according to the [input schema below](#input-file-schema). For our example Box Drive app, it would look like this:
```json
@@ -102,6 +102,138 @@ This is a filepath to an uninstall script. If provided, this script will be used
The script should be added to the `inputs/homebrew/scripts` directory.
## Adding a new app (Windows)
Use the Winget ingester. You will author:
- An input JSON in `ee/maintained-apps/inputs/winget/`
- Optional PowerShell scripts in `ee/maintained-apps/inputs/winget/scripts/`
### Can I do this on macOS?
The instructions below are meant to be run on a Windows host. But, you can run most of this on a macOS host, as well:
- You can author Windows inputs and run the generator on macOS. The ingester is Go code that fetches data from winget/GitHub and works crossplatform.
- To find a Winget PackageIdentifier without a Windows host, browse the winget-pkgs repo: https://github.com/microsoft/winget-pkgs (search for your apps manifests).
- To find the PackageName and Publisher, you can look in the locale and installer yaml files in the winget-pkgs repo.
- Validation and testing still require a Windows host (to verify programs.name and to run install/uninstall).
### Step 1: Find the Winget PackageIdentifier
- On a Windows host, run: `winget search <app name>`
- Note the `PackageIdentifier`. For example, Box Drive is typically `Box.Box`.
### Step 2: Find the unique identifier used by osquery
The Windows ingester expects `unique_identifier` to match the value in `programs.name` on the host after install (this is what Fleet uses to confirm the app exists).
- On a test Windows host, install the app manually, then check either:
- Fleet live query: `SELECT name, version, publisher FROM programs WHERE name LIKE '%<App Name>%';`
- PowerShell: `Get-ItemProperty 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*' | Where-Object {$_.DisplayName -like '*<App Name>*'} | Select-Object DisplayName, DisplayVersion, Publisher`
- Use the exact `DisplayName`/`programs.name` string as `unique_identifier`.
### Step 3: Choose installer metadata
If the winget manifest supports multiple installers, these fields select the right one:
- `installer_arch`: `x64` or `x86` (most apps use `x64`)
- `installer_type`: `exe`, `msi`, or `msix` (file type, not vendor tech like "wix")
- `installer_scope`: `machine` or `user` (prefer `machine` for managed installs)
- Optional: `installer_locale` if a specific locale is required
- Optional: `program_publisher`, `uninstall_type`, `fuzzy_match_name` (rare)
- `default_categories`: one or more of: `Browsers`, `Communication`, `Developer Tools`, `Productivity`
Tip: Setting these accurately avoids ambiguity when multiple installers exist.
### Step 4: Provide install/uninstall scripts
How scripts work:
- MSI installers: The ingester automatically generates install and uninstall scripts. Do not add scripts unless you need to override the generated behavior.
- EXE installers: You must provide PowerShell scripts that run the installer file directly. Fleet stores the installer and sends it to the host at install time; your script must execute it using the INSTALLER_PATH environment variable.
Place scripts in `ee/maintained-apps/inputs/winget/scripts/`.
Example install script `ee/maintained-apps/inputs/winget/scripts/box_drive_install.ps1`:
```powershell
# Install system-wide, silent
# Learn more about .exe install scripts:
# http://fleetdm.com/learn-more-about/exe-install-scripts
$exeFilePath = "${env:INSTALLER_PATH}"
try {
# Silent arguments vary by installer (e.g., /S, /silent, /VERYSILENT)
$processOptions = @{
FilePath = "$exeFilePath"
ArgumentList = "/VERYSILENT /NORESTART"
PassThru = $true
Wait = $true
}
$process = Start-Process @processOptions
$exitCode = $process.ExitCode
Write-Host "Install exit code: $exitCode"
Exit $exitCode
}
catch {
Write-Host "Error: $_"
Exit 1
}
```
Uninstall scripts for EXE installers are vendor-specific. Use the vendors documented silent uninstall switch or the registered UninstallString (if available), ensuring the script runs silently and returns the installers exit code.
For MSI installers, you can omit scripts; for EXE installers, scripts are required.
### Step 5: Create the Winget input JSON
Create `ee/maintained-apps/inputs/winget/box-drive.json`:
```json
{
"name": "Box Drive",
"slug": "box-drive/windows",
"package_identifier": "Box.Box",
"unique_identifier": "Box",
"installer_arch": "x64",
"installer_type": "msi",
"installer_scope": "machine",
"default_categories": ["Productivity"]
}
```
Notes:
- `slug` uses `<app-name>/windows` (lowercase, dash-separated name)
- `unique_identifier` must match `programs.name` exactly
### Step 6: Generate outputs
From the repo root:
```bash
go run cmd/maintained-apps/main.go --slug="box-drive/windows" --debug
```
This updates/creates:
- `ee/maintained-apps/outputs/apps.json` (catalog entry)
- `ee/maintained-apps/outputs/box-drive/windows.json` (manifest + script refs)
### Step 7: Add description
Edit `ee/maintained-apps/outputs/apps.json` to add a human-friendly `description` for your apps entry. For Windows entries, use the vendor description (from the winget manifest or vendor site).
### Step 8: Test in a Fleet instance
- Set an override to point your Fleet instance at your branchs catalog:
```bash
export FLEET_DEV_MAINTAINED_APPS_BASE_URL="https://raw.githubusercontent.com/<repository-name>/fleet/refs/heads/<PR-branch-name>/ee/maintained-apps/outputs"
```
- Trigger a refresh:
```bash
fleetctl trigger --name maintained_apps
```
- Add the app to a team, deploy to a Windows host, and verify:
- Install completes
- App launches
- App uninstalls cleanly
### Step 9: Open the PR
- Include “Fixes #<issue-number>” and any validation notes/screenshots
- The software group will review, validate, and merge when ready
### Troubleshooting (Windows)
- App not found in Fleet UI: ensure `apps.json` was updated by the generator and your override URL is correct
- Install fails silently: confirm your `installer_type`, `installer_arch`, and `installer_scope` match the selected winget installer; run your PowerShell script manually on a test host
- Uninstall doesnt remove the app: prefer explicit uninstall scripts; otherwise, ensure the winget manifest exposes `ProductCode` or `UpgradeCode`
- Hash mismatch errors: if the upstream manifest is in flux, you can set `ignore_hash: true` in the input JSON (use sparingly)
### Validating Fleet-maintained apps additions
1. When a pull request (PR) is opened containing changes to `ee/maintained-apps/inputs/`, the [#g-software Product Designer (PD) and Engineering Manager (EM)](https://fleetdm.com/handbook/company/product-groups#software-group) are automatically added as reviewers.