Public/Uninstall-sqmOdbcDriver.ps1

function Uninstall-sqmOdbcDriver {
    <#
    .SYNOPSIS
        Deinstalliert den Microsoft ODBC Driver for SQL Server.
    .DESCRIPTION
        Sucht den installierten Microsoft ODBC Driver for SQL Server in der
        Windows-Uninstall-Registry und fuehrt eine stille Deinstallation via
        msiexec /x durch. Wird typischerweise vor einer Neuinstallation einer
        neueren Version aufgerufen.
    .PARAMETER DriverName
        Optionaler Treibername fuer gezieltes Matching.
        Standard: Wildcard 'Microsoft ODBC Driver * for SQL Server'.
    .EXAMPLE
        Uninstall-sqmOdbcDriver
    .EXAMPLE
        Uninstall-sqmOdbcDriver -DriverName 'Microsoft ODBC Driver 17 for SQL Server'
    .NOTES
        Erfordert lokale Administratorrechte.
        ExitCode 1605 (nicht installiert) wird als NotFound behandelt, nicht als Fehler.
    #>

    [CmdletBinding()]
    param(
        [string]$DriverName = ''
    )

    $ErrorActionPreference = 'Stop'

    # Registry-Pfade fuer 64-bit und 32-bit Uninstall-Eintraege
    $uninstallPaths = @(
        'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*',
        'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
    )

    $pattern = if ($DriverName -and $DriverName -ne '') {
        [System.Text.RegularExpressions.Regex]::Escape($DriverName)
    } else {
        'Microsoft ODBC Driver \d+ for SQL Server'
    }

    $entry = $null
    foreach ($path in $uninstallPaths) {
        try {
            $found = Get-ItemProperty -Path $path -ErrorAction SilentlyContinue |
                     Where-Object { $_.DisplayName -match $pattern } |
                     Sort-Object { [int](($_.DisplayName -replace '\D','')) } -Descending |
                     Select-Object -First 1
            if ($found) { $entry = $found; break }
        } catch { }
    }

    if (-not $entry) {
        return [PSCustomObject]@{
            Status  = 'NotFound'
            Message = "Kein installierter ODBC-Treiber gefunden (Muster: $pattern)"
        }
    }

    $displayName   = $entry.DisplayName
    $productCode   = $entry.PSChildName  # z.B. {GUID}

    if (-not $productCode -or $productCode -notmatch '^\{[0-9A-Fa-f\-]+\}$') {
        # Fallback: UninstallString auslesen
        $uninstallStr = $entry.UninstallString
        if ($uninstallStr -match '\{[0-9A-Fa-f\-]+\}') {
            $productCode = $Matches[0]
        } else {
            return [PSCustomObject]@{
                Status  = 'Error'
                Message = "ProductCode konnte nicht ermittelt werden fuer: $displayName"
            }
        }
    }

    try {
        $proc = Start-Process -FilePath 'msiexec.exe' `
                              -ArgumentList "/x $productCode /quiet /norestart" `
                              -Wait -PassThru -ErrorAction Stop

        switch ($proc.ExitCode) {
            0     { return [PSCustomObject]@{ Status = 'Uninstalled'; Message = "OK: '$displayName' deinstalliert." } }
            1605  { return [PSCustomObject]@{ Status = 'NotFound';    Message = "Treiber war nicht installiert (ExitCode 1605)." } }
            3010  { return [PSCustomObject]@{ Status = 'Uninstalled'; Message = "OK: '$displayName' deinstalliert (Neustart empfohlen)." } }
            default {
                return [PSCustomObject]@{
                    Status  = 'Error'
                    Message = "msiexec ExitCode $($proc.ExitCode) bei Deinstallation von '$displayName'."
                }
            }
        }
    }
    catch {
        return [PSCustomObject]@{
            Status  = 'Error'
            Message = "Fehler bei Deinstallation von '$displayName': $_"
        }
    }
}