From 4036f9bfcb718b20cf1e43d87244d77a87bd48ed Mon Sep 17 00:00:00 2001 From: kitzy Date: Mon, 3 Aug 2026 10:44:54 -0400 Subject: [PATCH] Add Microsoft ODBC Driver 18 for SQL Server as a Windows Fleet-maintained app (#50348) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Related issue:** Resolves #50327 Adds Microsoft ODBC Driver 18 for SQL Server as a Windows Fleet-maintained app, from winget `Microsoft.msodbcsql.18` (18.6.2.1, MSI, machine scope, x64, en-US). Sibling of #50342 (driver 17); the two install side by side and are separate products. ## Verification Identity read from the MSI Property table: ``` ProductName Microsoft ODBC Driver 18 for SQL Server Manufacturer Microsoft Corporation ProductCode {820A3DEC-9783-42AE-B12D-750FCCF07E10} UpgradeCode {ADA68B65-BFF8-4E6A-B082-CC6682D425B8} ALLUSERS 1 ``` - Installer SHA confirmed against a local download (`20314529…4b82`). - The UpgradeCode differs from driver 17's (`{0123A210-…}`), which confirms they are independent products rather than upgrades of one another. The generated uninstall script picked up 18's, so uninstalling one will not touch the other. - Because `ProductName` already carries the major version, the exists query is a simple equality — no `version LIKE '18.%'` pinning needed, and no risk of 17 and 18 matching each other. Like driver 17, the MSI refuses to install without `IACCEPTMSODBCSQLLICENSETERMS=YES`, which Fleet's default MSI script does not pass, so this ships a small custom install script. It is deliberately a **separate file** from driver 17's rather than a shared path, following the repo's one-script-per-app convention and keeping the two PRs independently mergeable. The icon comes from the MSI's own `ARPPRODUCTICON` stream. As with #50342 it is natively 32×32, so the 128×128 asset is an upscale — happy to drop it in favour of the generic fallback if reviewers prefer. The icon map key also needed the same manual correction (the generator derives it from the slug, which omits "for sql server"). The manifest declares a `Microsoft.VCRedist.2015+.x64` dependency, which the ingester ignores; we ship `vc-redist-x64/windows` and the redistributable is present on most hosts. # Checklist for submitter - [x] QA'd all new/changed functionality manually ## Summary by CodeRabbit * **New Features** * Added Microsoft ODBC Driver 18 for SQL Server to the maintained Windows software catalog. * Added support for version 18.6.2.1, including installation, upgrade, uninstall, detection, and license acceptance handling. * Added the software’s icon and catalog display details. * Included support for SQL Server and Azure SQL connectivity scenarios. * Installation now provides appropriate handling for successful completion, restart requirements, and installation failures. Co-authored-by: Allen Houchins --- .../winget/microsoft-odbc-driver-18.json | 11 +++++++++ .../microsoft_odbc_driver_18_install.ps1 | 22 +++++++++++++++++ ee/maintained-apps/outputs/apps.json | 7 ++++++ .../microsoft-odbc-driver-18/windows.json | 23 ++++++++++++++++++ .../icons/MicrosoftOdbcDriver18.tsx | 14 +++++++++++ .../SoftwarePage/components/icons/index.ts | 2 ++ ...icon-microsoft-odbc-driver-18-60x60@2x.png | Bin 0 -> 9169 bytes 7 files changed, 79 insertions(+) create mode 100644 ee/maintained-apps/inputs/winget/microsoft-odbc-driver-18.json create mode 100644 ee/maintained-apps/inputs/winget/scripts/microsoft_odbc_driver_18_install.ps1 create mode 100644 ee/maintained-apps/outputs/microsoft-odbc-driver-18/windows.json create mode 100644 frontend/pages/SoftwarePage/components/icons/MicrosoftOdbcDriver18.tsx create mode 100644 website/assets/images/app-icon-microsoft-odbc-driver-18-60x60@2x.png diff --git a/ee/maintained-apps/inputs/winget/microsoft-odbc-driver-18.json b/ee/maintained-apps/inputs/winget/microsoft-odbc-driver-18.json new file mode 100644 index 0000000000..3edb77bf6b --- /dev/null +++ b/ee/maintained-apps/inputs/winget/microsoft-odbc-driver-18.json @@ -0,0 +1,11 @@ +{ + "name": "Microsoft ODBC Driver 18 for SQL Server", + "slug": "microsoft-odbc-driver-18/windows", + "package_identifier": "Microsoft.msodbcsql.18", + "unique_identifier": "Microsoft ODBC Driver 18 for SQL Server", + "install_script_path": "ee/maintained-apps/inputs/winget/scripts/microsoft_odbc_driver_18_install.ps1", + "installer_arch": "x64", + "installer_type": "msi", + "installer_scope": "machine", + "default_categories": ["Developer tools"] +} diff --git a/ee/maintained-apps/inputs/winget/scripts/microsoft_odbc_driver_18_install.ps1 b/ee/maintained-apps/inputs/winget/scripts/microsoft_odbc_driver_18_install.ps1 new file mode 100644 index 0000000000..8bac7607b1 --- /dev/null +++ b/ee/maintained-apps/inputs/winget/scripts/microsoft_odbc_driver_18_install.ps1 @@ -0,0 +1,22 @@ +# The MSI refuses to install without IACCEPTMSODBCSQLLICENSETERMS=YES, which the +# default MSI install script does not pass. + +$logFile = "${env:TEMP}/fleet-install-software.log" + +try { + +$installProcess = Start-Process msiexec.exe ` + -ArgumentList "/quiet /norestart /lv ${logFile} /i `"${env:INSTALLER_PATH}`" IACCEPTMSODBCSQLLICENSETERMS=YES" ` + -PassThru -Verb RunAs -Wait + +Get-Content $logFile -Tail 500 + +# 3010 (reboot required) and 1641 (reboot initiated) are successful installs. +if ($installProcess.ExitCode -eq 3010 -or $installProcess.ExitCode -eq 1641) { Exit 0 } + +Exit $installProcess.ExitCode + +} catch { + Write-Host "Error: $_" + Exit 1 +} diff --git a/ee/maintained-apps/outputs/apps.json b/ee/maintained-apps/outputs/apps.json index aaea151904..06c5533efb 100644 --- a/ee/maintained-apps/outputs/apps.json +++ b/ee/maintained-apps/outputs/apps.json @@ -5461,6 +5461,13 @@ "unique_identifier": "Microsoft ODBC Driver 17 for SQL Server", "description": "Microsoft ODBC Driver 17 for SQL Server lets applications connect to SQL Server and Azure SQL Database through the ODBC interface." }, + { + "name": "Microsoft ODBC Driver 18 for SQL Server", + "slug": "microsoft-odbc-driver-18/windows", + "platform": "windows", + "unique_identifier": "Microsoft ODBC Driver 18 for SQL Server", + "description": "Microsoft ODBC Driver 18 for SQL Server lets applications connect to SQL Server and Azure SQL Database through the ODBC interface." + }, { "name": "Microsoft Office", "slug": "microsoft-office/windows", diff --git a/ee/maintained-apps/outputs/microsoft-odbc-driver-18/windows.json b/ee/maintained-apps/outputs/microsoft-odbc-driver-18/windows.json new file mode 100644 index 0000000000..718531e3c6 --- /dev/null +++ b/ee/maintained-apps/outputs/microsoft-odbc-driver-18/windows.json @@ -0,0 +1,23 @@ +{ + "versions": [ + { + "version": "18.6.2.1", + "queries": { + "exists": "SELECT 1 FROM programs WHERE name = 'Microsoft ODBC Driver 18 for SQL Server' AND publisher = 'Microsoft Corporation';", + "patched": "SELECT 1 WHERE NOT EXISTS (SELECT 1 FROM programs WHERE name = 'Microsoft ODBC Driver 18 for SQL Server' AND publisher = 'Microsoft Corporation' AND version_compare(version, '18.6.2.1') < 0);" + }, + "installer_url": "https://download.microsoft.com/download/7bf9fad4-0f21-486d-a750-fc990ded5624/amd64/1033/msodbcsql.msi", + "install_script_ref": "5a2f9a9f", + "uninstall_script_ref": "28feb158", + "sha256": "20314529110da3365a252164a657bdc837a18be5839105aa5f5acf0a8d2f4b82", + "default_categories": [ + "Developer tools" + ], + "upgrade_code": "{ADA68B65-BFF8-4E6A-B082-CC6682D425B8}" + } + ], + "refs": { + "28feb158": "# Fleet uninstalls app by finding all related product codes for the specified upgrade code\n$inst = New-Object -ComObject \"WindowsInstaller.Installer\"\n$timeoutSeconds = 300 # 5 minute timeout per product\n\n# MSI exit codes that indicate success. 3010 = ERROR_SUCCESS_REBOOT_REQUIRED,\n# 1641 = ERROR_SUCCESS_REBOOT_INITIATED. Treat these as success rather than failure.\n$successCodes = @(0, 3010, 1641)\n\nforeach ($product_code in $inst.RelatedProducts('{ADA68B65-BFF8-4E6A-B082-CC6682D425B8}')) {\n $process = Start-Process msiexec -ArgumentList @(\"/quiet\", \"/x\", $product_code, \"/norestart\") -PassThru\n\n # Wait for process with timeout\n $completed = $process.WaitForExit($timeoutSeconds * 1000)\n\n if (-not $completed) {\n Stop-Process -Id $process.Id -Force -ErrorAction SilentlyContinue\n Exit 1603 # ERROR_UNINSTALL_FAILURE\n }\n\n # If the uninstall failed, bail\n if ($successCodes -notcontains $process.ExitCode) {\n Write-Output \"Uninstall for $($product_code) exited $($process.ExitCode)\"\n Exit $process.ExitCode\n }\n}\n\n# All uninstalls succeeded; exit success\nExit 0\n", + "5a2f9a9f": "# The MSI refuses to install without IACCEPTMSODBCSQLLICENSETERMS=YES, which the\n# default MSI install script does not pass.\n\n$logFile = \"${env:TEMP}/fleet-install-software.log\"\n\ntry {\n\n$installProcess = Start-Process msiexec.exe `\n -ArgumentList \"/quiet /norestart /lv ${logFile} /i `\"${env:INSTALLER_PATH}`\" IACCEPTMSODBCSQLLICENSETERMS=YES\" `\n -PassThru -Verb RunAs -Wait\n\nGet-Content $logFile -Tail 500\n\n# 3010 (reboot required) and 1641 (reboot initiated) are successful installs.\nif ($installProcess.ExitCode -eq 3010 -or $installProcess.ExitCode -eq 1641) { Exit 0 }\n\nExit $installProcess.ExitCode\n\n} catch {\n Write-Host \"Error: $_\"\n Exit 1\n}\n" + } +} diff --git a/frontend/pages/SoftwarePage/components/icons/MicrosoftOdbcDriver18.tsx b/frontend/pages/SoftwarePage/components/icons/MicrosoftOdbcDriver18.tsx new file mode 100644 index 0000000000..c11485973f --- /dev/null +++ b/frontend/pages/SoftwarePage/components/icons/MicrosoftOdbcDriver18.tsx @@ -0,0 +1,14 @@ +import * as React from "react"; + +import type { SVGProps } from "react"; + +const MicrosoftOdbcDriver18 = (props: SVGProps) => ( + + + +); +export default MicrosoftOdbcDriver18; diff --git a/frontend/pages/SoftwarePage/components/icons/index.ts b/frontend/pages/SoftwarePage/components/icons/index.ts index ba300231d2..7aeff9c3ef 100644 --- a/frontend/pages/SoftwarePage/components/icons/index.ts +++ b/frontend/pages/SoftwarePage/components/icons/index.ts @@ -646,6 +646,7 @@ import MicrosoftAzureStorageExplorer from "./MicrosoftAzureStorageExplorer"; import MicrosoftDotnetRuntime from "./MicrosoftDotnetRuntime"; import MicrosoftEdge from "./MicrosoftEdge"; import MicrosoftOdbcDriver17 from "./MicrosoftOdbcDriver17"; +import MicrosoftOdbcDriver18 from "./MicrosoftOdbcDriver18"; import MicrosoftOffice from "./MicrosoftOffice"; import MicrosoftOneNote from "./MicrosoftOneNote"; import MicrosoftOutlook from "./MicrosoftOutlook"; @@ -1804,6 +1805,7 @@ export const SOFTWARE_NAME_TO_ICON_MAP = { "microsoft edge": Edge, "microsoft excel": Excel, "microsoft odbc driver 17 for sql server": MicrosoftOdbcDriver17, + "microsoft odbc driver 18 for sql server": MicrosoftOdbcDriver18, "microsoft office": MicrosoftOffice, "microsoft onenote": MicrosoftOneNote, "microsoft outlook": MicrosoftOutlook, diff --git a/website/assets/images/app-icon-microsoft-odbc-driver-18-60x60@2x.png b/website/assets/images/app-icon-microsoft-odbc-driver-18-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..530593b73293ffb34b9636fa4d0ba3370a83e6a8 GIT binary patch literal 9169 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