Files
kitzy 2fc41c7592 Add Azure Data Studio as a Windows FMA (#50027)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** #50020

# What this does

Adds **Azure Data Studio** as a Windows Fleet-maintained app. One of the
11 apps split out of #48501 that failed the FMA validator; #50016
shipped the 6 that passed.

## Why it was failing

The install itself worked — the validator logged `New application
detected at: C:\Program Files\Azure Data Studio`. The *script* never
returned:

```
20:08:19  INFO  msg="Executing install script..." app="Azure Data Studio"
20:18:19  ERROR msg="Error executing install script: exit status 1"   # exactly 10:00 later
20:18:19  INFO  msg="New application detected at: C:\Program Files\Azure Data Studio"
```

Ten minutes on the nose is the validator's `executeScript` timeout.
Azure Data Studio is a Visual Studio Code fork and ships the same Inno
Setup script — including the **`runcode` task, which launches the app
when the install finishes**. Because `Start-Process -Wait` waits for the
process *and all of its descendants*, the launched app kept the script
blocked forever.

The fix is the switch VS Code's own FMA already uses:
`/MERGETASKS=!runcode` (see
[`vscode_install.ps1`](ee/maintained-apps/inputs/winget/scripts/vscode_install.ps1)
and
[`vscodium_install.ps1`](ee/maintained-apps/inputs/winget/scripts/vscodium_install.ps1),
both of which pass validation). The script also waits on the installer
process alone rather than its descendants, polls for the Add/Remove
Programs entry, and stops a stray `azuredatastudio` process as a
backstop in case a future build ignores the task suppression.

## Notes

- Machine-scope x64 installer, per the winget manifest — Azure Data
Studio publishes both user and machine scope, and Fleet installs run as
SYSTEM, so machine scope is required.
- Clean ARP `DisplayName` (`Azure Data Studio`), so exact name matching.
Publisher `Microsoft Corporation`.
- Uninstall is unchanged: the Inno uninstaller doesn't leave anything
resident, and `-Wait` waiting on descendants is the desired behavior
there (Inno relaunches itself from `%TEMP%`).
- Ships a new catalog icon and website asset.

# Checklist for submitter

- [x] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements), JS
inline code is prevented especially for url redirects, and untrusted
data interpolated into shell scripts/commands is validated against shell
metacharacters.
- [x] Timeouts are implemented and retries are limited to avoid infinite
loops

## Testing

- [x] FMA CI validator (install → detect → uninstall) **passes** on the
SYSTEM-context Windows runner — [run
30384162280](https://github.com/fleetdm/fleet/actions/runs/30384162280)
(`All checks passed`)
- [x] Generated output verified locally: manifest SHA matches the winget
manifest, exists/patched queries reviewed for name + publisher
correctness, `apps.json` is valid JSON with a description filled in.
- [x] QA'd all new/changed functionality manually




<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
  * Added Azure Data Studio to the available Windows software catalog.
* Added support for installing and uninstalling Azure Data Studio
(version 1.52.0) via silent installer and uninstaller flows with
completion detection.
* Added an Azure Data Studio icon to the software interface for better
visual identification.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-28 22:56:00 -05:00

76 lines
2.7 KiB
PowerShell

# Learn more about .exe install scripts:
# http://fleetdm.com/learn-more-about/exe-install-scripts
$exeFilePath = "${env:INSTALLER_PATH}"
# ADS is a VS Code fork with the same Inno script, including the "runcode" task
# that launches the app after install. -Wait waits on descendants, so that would
# block forever; "/MERGETASKS=!runcode" suppresses the launch, as in
# vscode_install.ps1. Timeouts are sized to stay under the caller's 10-minute cap.
$installTimeoutSeconds = 420
$registrationTimeoutSeconds = 120
$machineKey = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*'
$machineKey32on64 = 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
function Test-AzureDataStudioRegistered {
$null -ne (Get-ChildItem -Path @($machineKey, $machineKey32on64) -ErrorAction SilentlyContinue |
ForEach-Object { Get-ItemProperty $_.PSPath -ErrorAction SilentlyContinue } |
Where-Object { $_.DisplayName -like "Azure Data Studio*" } |
Select-Object -First 1)
}
try {
$process = Start-Process -FilePath "$exeFilePath" `
-ArgumentList "/VERYSILENT /SUPPRESSMSGBOXES /NORESTART /MERGETASKS=!runcode" `
-PassThru
# Keeps .ExitCode readable after the process ends.
$null = $process.Handle
$killed = $false
if (-not $process.WaitForExit($installTimeoutSeconds * 1000)) {
Write-Host "Installer process did not exit within ${installTimeoutSeconds}s, stopping it."
Stop-Process -Id $process.Id -Force -ErrorAction SilentlyContinue
# Reading .ExitCode while the process is alive would throw.
$null = $process.WaitForExit(30 * 1000)
$killed = $true
}
$exitCode = $null
if ($process.HasExited) {
$exitCode = $process.ExitCode
Write-Host "Install exit code: $exitCode"
} else {
Write-Host "Installer process could not be stopped; falling back to the registration check."
}
# The installer can return before the ARP entry is written.
$elapsed = 0
while (-not (Test-AzureDataStudioRegistered) -and ($elapsed -lt $registrationTimeoutSeconds)) {
Start-Sleep -Seconds 5
$elapsed += 5
Write-Host "Waiting for Azure Data Studio to register... ($elapsed seconds)"
}
# In case a future build ignores !runcode.
Stop-Process -Name "azuredatastudio" -Force -ErrorAction SilentlyContinue
if (-not (Test-AzureDataStudioRegistered)) {
Write-Host "Azure Data Studio did not register in Add/Remove Programs."
Exit 1
}
# Registration above is the success signal; a killed process's code means nothing.
if ($killed -or $null -eq $exitCode) { Exit 0 }
# 3010 (reboot required) and 1641 (reboot initiated) are successful installs.
if ($exitCode -eq 3010 -or $exitCode -eq 1641) { Exit 0 }
Exit $exitCode
} catch {
Write-Host "Error: $_"
Exit 1
}