Public/Uninstall-VcRedist.ps1

function Uninstall-VcRedist {
    <#
        .EXTERNALHELP VcRedist-help.xml
    #>

    [CmdletBinding(DefaultParameterSetName = "Manual", SupportsShouldProcess = $true, ConfirmImpact = "High", HelpURI = "https://vcredist.com/uninstall-vcredist/")]
    [OutputType([System.Management.Automation.PSObject])]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSReviewUnusedParameter", "")]
    param (
        [Parameter(Mandatory = $false, Position = 0, ParameterSetName = "Manual")]
        [ValidateSet("2005", "2008", "2010", "2012", "2013", "2015", "2017", "2019", "14")]
        [System.String[]] $Release = @("2005", "2008", "2010", "2012", "2013", "2015", "2017", "2019", "14"),

        [Parameter(Mandatory = $false, Position = 1, ParameterSetName = "Manual")]
        [ValidateSet("x86", "x64", "ARM64")]
        [System.String[]] $Architecture = @("x86", "x64"),

        [Parameter(
            Mandatory = $true,
            Position = 0,
            ValueFromPipeline,
            ParameterSetName = "Pipeline",
            HelpMessage = "Pass a VcList object from Get-VcList.")]
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.PSObject] $VcList
    )

    begin {
        # Get script elevation status
        [System.Boolean] $Elevated = Test-Elevated
        if ($Elevated -eq $false) {
            $Msg = "Uninstalling the Visual C++ Redistributables requires elevation. The current Windows PowerShell session is not running as Administrator. Start Windows PowerShell by using the Run as Administrator option, and then try running the script again"
            throw [System.Management.Automation.ScriptRequiresException]::New($Msg)
        }
    }

    process {
        switch ($PSCmdlet.ParameterSetName) {
            "Manual" {
                # Get the installed VcRedists and filter
                Write-Verbose -Message "Getting locally installed Visual C++ Redistributables"
                $VcRedistsToRemove = Get-InstalledVcRedist | Where-Object { $Release -contains $_.Release } | Where-Object { $Architecture -contains $_.Architecture }
            }
            "Pipeline" {
                Write-Verbose -Message "Removing installed Visual C++ Redistributables passed via the pipeline"
                $VcRedistsToRemove = $VcList
            }
        }

        # Walk through each VcRedist and uninstall
        foreach ($VcRedist in $VcRedistsToRemove) {
            $UninstallString = $null
            $Result = $null
            $State = $null
            $params = $null

            # We could be passed an object from Get-VcList or Get InstalledVcRedist
            if ([System.String]::IsNullOrEmpty($VcRedist.UninstallString) -eq $false) {
                $UninstallString = $VcRedist.UninstallString
            }
            elseif ([System.String]::IsNullOrEmpty($VcRedist.SilentUninstall) -eq $false) {
                $UninstallString = $VcRedist.SilentUninstall
            }

            if ([System.String]::IsNullOrEmpty($UninstallString)) {
                $Msg = "Cannot find uninstall string. Please check object passed to this function."
                throw [System.Management.Automation.PropertyNotFoundException]::New($Msg)
            }

            # Build the uninstall command
            if ($UninstallString -match "^\s*`"?msiexec(\.exe)?`"?\s") {
                if ($VcRedist.ProductCode -notmatch "^\{[0-9A-Fa-f]{8}(-[0-9A-Fa-f]{4}){3}-[0-9A-Fa-f]{12}\}$") {
                    Write-Warning -Message "Invalid product code for '$($VcRedist.Name)': '$($VcRedist.ProductCode)'."
                    continue
                }
                Write-Verbose -Message "VcRedist uninstall uses Msiexec."
                $params = @{
                    FilePath     = "$Env:SystemRoot\System32\msiexec.exe"
                    ArgumentList = "/uninstall $($VcRedist.ProductCode) /quiet /norestart"
                }
            }
            else {
                # Split the command into the executable (quoted or not), expanding environment variables
                $FilePath = $null
                if ($UninstallString -match "^\s*`"([^`"]+)`"") { $FilePath = $Matches[1] }
                elseif ($UninstallString -match "^\s*(\S+)") { $FilePath = $Matches[1] }
                $FilePath = [System.Environment]::ExpandEnvironmentVariables([System.String]$FilePath)

                if (-not (Test-Path -LiteralPath $FilePath -PathType "Leaf")) {
                    Write-Warning -Message "Cannot find uninstaller '$FilePath' for $($VcRedist.Name) $($VcRedist.Version) $($VcRedist.Architecture)."
                    $State = "Failed"
                }
                else {
                    Write-Verbose -Message "VcRedist uninstall uses '$FilePath'."
                    $params = @{
                        FilePath     = $FilePath
                        ArgumentList = "/uninstall /quiet /norestart"
                    }
                }
            }

            if ($null -ne $params -and $PSCmdlet.ShouldProcess($VcRedist.Name, "Uninstall")) {
                try {
                    $params.PassThru = $true
                    $params.Wait = $true
                    $params.NoNewWindow = $true
                    $params.ErrorAction = "Stop"
                    $Result = Start-Process @params

                    # 3010: reboot required; 1641: reboot initiated; 1605: product not installed
                    if (@(0, 1605, 1641, 3010) -contains $Result.ExitCode) {
                        $State = "Uninstalled"
                    }
                    else {
                        Write-Warning -Message "Uninstall of $($VcRedist.Name) $($VcRedist.Version) $($VcRedist.Architecture) returned exit code $($Result.ExitCode)."
                        $State = "Failed"
                    }
                }
                catch [System.Exception] {
                    Write-Warning -Message "Failure in uninstalling $($VcRedist.Name) $($VcRedist.Version) $($VcRedist.Architecture): $($_.Exception.Message)"
                    $State = "Failed"
                }
            }

            if ($null -ne $State) {
                [PSCustomObject] @{
                    Name         = $VcRedist.Name
                    Version      = $VcRedist.Version
                    Release      = $VcRedist.Release
                    Architecture = $VcRedist.Architecture
                    State        = $State
                    ExitCode     = $Result.ExitCode
                }
            }
        }
    }

    end {
    }
}