Files
fleet/ee/maintained-apps/inputs/winget/scripts/azure_data_studio_uninstall.ps1
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

37 lines
2.1 KiB
PowerShell

$softwareName = "Azure Data Studio"
$softwareNameLike = "$softwareName*"
$softwarePublisher = "Microsoft Corporation"
$uninstallArgs = "/VERYSILENT /SUPPRESSMSGBOXES /NORESTART"
$machineKey = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*'
$machineKey32on64 = 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
$exitCode = 0
try {
[array]$uninstallKeys = Get-ChildItem -Path @($machineKey, $machineKey32on64) -ErrorAction SilentlyContinue |
ForEach-Object { Get-ItemProperty $_.PSPath -ErrorAction SilentlyContinue }
$foundUninstaller = $false
foreach ($key in $uninstallKeys) {
if ($key.DisplayName -like $softwareNameLike -and $key.Publisher -eq $softwarePublisher) {
$foundUninstaller = $true
$uninstallCommand = if ($key.QuietUninstallString) { $key.QuietUninstallString } else { $key.UninstallString }
if ($uninstallCommand -match '^\s*"([^"]+)"\s*(.*)$') {
$uninstallCommand = $Matches[1]; if ($Matches[2]) { $uninstallArgs = "$($Matches[2]) $uninstallArgs".Trim() }
} elseif ($uninstallCommand -match '(?i)^\s*(.+?\.exe)\s*(.*)$') {
$uninstallCommand = $Matches[1]; if ($Matches[2]) { $uninstallArgs = "$($Matches[2]) $uninstallArgs".Trim() }
} elseif ($uninstallCommand -match '^\s*(\S+)\s*(.*)$') {
$uninstallCommand = $Matches[1]; if ($Matches[2]) { $uninstallArgs = "$($Matches[2]) $uninstallArgs".Trim() }
}
Write-Host "Uninstall command: $uninstallCommand"; Write-Host "Uninstall args: $uninstallArgs"
$processOptions = @{ FilePath = $uninstallCommand; PassThru = $true; Wait = $true }
if ($uninstallArgs -ne '') { $processOptions.ArgumentList = $uninstallArgs }
$process = Start-Process @processOptions
$exitCode = $process.ExitCode; Write-Host "Uninstall exit code: $exitCode"; break
}
}
if (-not $foundUninstaller) { Write-Host "Uninstall entry not found for '$softwareName'."; Exit 0 }
} catch { Write-Host "Error: $_"; Exit 1 }
Exit $exitCode