diff --git a/ee/maintained-apps/README.md b/ee/maintained-apps/README.md index 6f33a55e94..5ee4750049 100644 --- a/ee/maintained-apps/README.md +++ b/ee/maintained-apps/README.md @@ -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 cross‑platform. +- To find a Winget PackageIdentifier without a Windows host, browse the winget-pkgs repo: https://github.com/microsoft/winget-pkgs (search for your app’s 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 ` +- 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 '%%';` + - PowerShell: `Get-ItemProperty 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*' | Where-Object {$_.DisplayName -like '**'} | 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 vendor’s documented silent uninstall switch or the registered UninstallString (if available), ensuring the script runs silently and returns the installer’s 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 `/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 app’s 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 branch’s catalog: +```bash +export FLEET_DEV_MAINTAINED_APPS_BASE_URL="https://raw.githubusercontent.com//fleet/refs/heads//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 #” 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 doesn’t 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.