Files
Allen Houchins 1f2623817f Add several Windows FMAs (#47037)
This pull request adds support for several new Windows applications to
the maintained apps catalog, along with robust PowerShell install and
uninstall scripts for each. The main focus is on enabling automated,
silent deployment and removal of these apps using Winget, with careful
handling of installer quirks and registry lookups to ensure reliability
in enterprise environments.

**New application support:**

* Added `Citrix Workspace`, `Evernote`, `ExpressVPN`, `Front`, `Hyper`,
`Jabra Direct`, `Microsoft Teams`, `OneDrive`, `Opera`, `Podman
Desktop`, and `REAPER` to the `ee/maintained-apps/inputs/winget` catalog
with appropriate metadata for each app.
[[1]](diffhunk://#diff-3ba331a553f359a02dd3d824d3edab85292e2d1d9c1a6a739e313a9b63d5a88fR1-R13)
[[2]](diffhunk://#diff-917d3e25bae801c48ae70f70bf43e8540d962046bb773dbeed633eb52323e084R1-R12)
[[3]](diffhunk://#diff-b8f4026fc2a89254c0bdeb905120761ee78c6a76101deaf3dadd814ce3cafa94R1-R12)
[[4]](diffhunk://#diff-72a2bcc2c2b9fadcf09505f8caefb681e79679ee99fb068a84698c783e81f17dR1-R12)
[[5]](diffhunk://#diff-5bdb4220ac0cc23ff96963f975a55ef081493ee797545e0a1f65f9f4c50ef982R1-R12)
[[6]](diffhunk://#diff-941487a1c1f2ba4d2da50e47f47bbab46478f2bb7c7e009fdd21bf2b68090413R1-R12)
[[7]](diffhunk://#diff-9b5681ffd0b55fd89ce4f69e07668fa23df9582a921bf2000d9d2230429e16c9R1-R12)
[[8]](diffhunk://#diff-b69363f34d224c471ec183bf7a2d6bab13d776c8905e499cac0e39f31868fd19R1-R12)
[[9]](diffhunk://#diff-c8b49c5881565261369d37c862e055087cc65819900f961841c7dc76a83a879dR1-R13)
[[10]](diffhunk://#diff-88ceb0a6b8de6484bfcb3f0d19264e307d3166a34f992eba11fcc9d60facb052R1-R13)
[[11]](diffhunk://#diff-2557d37d79e9889f60f30557f28efb327e02bf33d4b255deefa912f855111412R1-R14)

**Install script implementations:**

* Added PowerShell install scripts for each new app, ensuring silent,
machine- or user-scoped installs as appropriate, with correct handling
of installer arguments and exit codes (including treating
reboot-required codes as success for Citrix Workspace and ExpressVPN).
[[1]](diffhunk://#diff-ccd33aec25bdbda547e3c4c9b4bcc32d923dcacfe6ebda1a80256d3388bb9003R1-R41)
[[2]](diffhunk://#diff-2b0d909e843c3c8265f67dd0e69843c4b59cea7d33741c5be8662ad464f39832R1-R34)
[[3]](diffhunk://#diff-fa9d859aa98e8240b9882363a62dc7c69a61186d186cf07c8065a8e11547a3c7R1-R37)
[[4]](diffhunk://#diff-9baf7c34c008963410a30fec1e6949023d03d3c400ec0166c029dd5eebcfaefeR1-R27)

**Uninstall script implementations:**

* Added PowerShell uninstall scripts for each new app, using registry
lookups to locate the correct uninstaller and applying silent uninstall
arguments. Scripts handle argument parsing defensively and ensure
correct removal in both per-user and per-machine contexts.
[[1]](diffhunk://#diff-ab38143c2c6f191c4f57aff0adb35e8c7a67cd95522da13da25febc8f65cc73eR1-R92)
[[2]](diffhunk://#diff-4657653f749298d4ae84fb467023108a273c8e621c8cc711d5821fbeacfa03cdR1-R91)
[[3]](diffhunk://#diff-1280127be173a1bdce9e7f18343fa6aae26fc3d15e1935e196669131deb484bdR1-R89)

These additions significantly expand the catalog's Windows app coverage
and improve reliability and automation for enterprise deployments.
2026-06-07 12:14:02 -05:00

100 lines
4.0 KiB
PowerShell

# MSIX: provision machine-wide so the app is available to all users at sign-in, then
# opportunistically register for the currently logged-on console user (via a scheduled
# task in their session) so the app is immediately visible without requiring sign-out.
#
# The Fleet agent runs as Local System on Windows, and Add-AppxPackage cannot run in that
# context (HRESULT 0x80073CF9). The scheduled task is the supported way to register a
# package in a user session from a system-context script.
$softwareName = "WindowsApp"
$taskName = "fleet-install-$softwareName.msix"
$scriptPath = "$env:PUBLIC\install-$softwareName.ps1"
$exitCodeFile = "$env:PUBLIC\install-exitcode-$softwareName.txt"
try {
$msixPath = $env:INSTALLER_PATH
if (-not $msixPath) {
throw "INSTALLER_PATH is not set"
}
Write-Host "Provisioning MSIX for all users..."
$result = Add-AppxProvisionedPackage -Online -PackagePath $msixPath -SkipLicense -ErrorAction Stop
$result | Out-String | Write-Host
# Win32_ComputerSystem.UserName returns the console user (DOMAIN\User) or null when no
# interactive session is active. Other RDP/fast-user-switch sessions won't get the
# immediate registration; those users will pick it up from the provisioned install at
# their next sign-in.
$userName = (Get-CimInstance Win32_ComputerSystem).UserName
if (-not $userName -or $userName -notlike "*\*") {
Write-Host "No interactive user logged on; provisioned install will register for each user at sign-in."
Start-Sleep -Seconds 5
Exit 0
}
Write-Host "Registering MSIX for logged-on user '$userName' via scheduled task..."
$userScript = @"
`$msixPath = "$msixPath"
`$exitCodeFile = "$exitCodeFile"
try {
Add-AppxPackage -Path `$msixPath -ErrorAction Stop | Out-String | Write-Host
Set-Content -Path `$exitCodeFile -Value 0
} catch {
Write-Host "Add-AppxPackage failed: `$(`$_.Exception.Message)"
Set-Content -Path `$exitCodeFile -Value 1
}
"@
Set-Content -Path $scriptPath -Value $userScript -Force
$action = New-ScheduledTaskAction -Execute "powershell.exe" `
-Argument "-WindowStyle Hidden -ExecutionPolicy Bypass -File `"$scriptPath`""
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries
$principal = New-ScheduledTaskPrincipal -UserId $userName -RunLevel Highest
$task = New-ScheduledTask -Action $action -Settings $settings -Principal $principal
Register-ScheduledTask -TaskName $taskName -InputObject $task -User $userName -Force | Out-Null
Start-ScheduledTask -TaskName $taskName
$startDate = Get-Date
$state = (Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue).State
while ($state -ne "Running") {
Start-Sleep -Seconds 1
if ((New-Timespan -Start $startDate).TotalSeconds -gt 30) {
Write-Host "Per-user registration task did not start within 30s; provisioned install is still valid."
break
}
$state = (Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue).State
}
while ($state -eq "Running") {
Start-Sleep -Seconds 2
if ((New-Timespan -Start $startDate).TotalSeconds -gt 90) {
Write-Host "Per-user registration task did not complete within 90s; provisioned install is still valid."
break
}
$state = (Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue).State
}
if (Test-Path $exitCodeFile) {
$code = (Get-Content $exitCodeFile -ErrorAction SilentlyContinue | Select-Object -First 1).Trim()
if ($code -eq "0") {
Write-Host "Per-user registration completed for '$userName'."
} else {
Write-Host "Per-user registration did not complete cleanly (exit code: $code). Provisioned install is still valid."
}
}
Start-Sleep -Seconds 5
Exit 0
} catch {
Write-Host "Error: $_"
Exit 1
} finally {
Unregister-ScheduledTask -TaskName $taskName -Confirm:$false -ErrorAction SilentlyContinue | Out-Null
Remove-Item -Path $scriptPath -Force -ErrorAction SilentlyContinue
Remove-Item -Path $exitCodeFile -Force -ErrorAction SilentlyContinue
}