Skip to content

Instantly share code, notes, and snippets.

@broestls
Last active May 15, 2026 17:30
Show Gist options
  • Select an option

  • Save broestls/f872872a00acee2fca02017160840624 to your computer and use it in GitHub Desktop.

Select an option

Save broestls/f872872a00acee2fca02017160840624 to your computer and use it in GitHub Desktop.
Force removal of VMware Tools, Program Files, and Windows Services
# This script will manually rip out all VMware Tools registry entries and files for Windows 2008-2019
# Tested for 2019, 2016, and probably works on 2012 R2 after the 2016 fixes.
# This function pulls out the common ID used for most of the VMware registry entries along with the ID
# associated with the MSI for VMware Tools.
function Get-VMwareToolsInstallerID {
foreach ($item in $(Get-ChildItem Registry::HKEY_CLASSES_ROOT\Installer\Products)) {
If ($item.GetValue('ProductName') -eq 'VMware Tools') {
return @{
reg_id = $item.PSChildName;
msi_id = [Regex]::Match($item.GetValue('ProductIcon'), '(?<={)(.*?)(?=})') | Select-Object -ExpandProperty Value
}
}
}
}
$vmware_tools_ids = Get-VMwareToolsInstallerID
# Targets we can hit with the common registry ID from $vmware_tools_ids.reg_id
$reg_targets = @(
"Registry::HKEY_CLASSES_ROOT\Installer\Features\",
"Registry::HKEY_CLASSES_ROOT\Installer\Products\",
"HKLM:\SOFTWARE\Classes\Installer\Features\",
"HKLM:\SOFTWARE\Classes\Installer\Products\",
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\"
)
$VMware_Tools_Directory = "C:\Program Files\VMware"
$VMware_Common_Directory = "C:\Program Files\Common Files\VMware"
# Create an empty array to hold all the uninstallation targets and compose the entries into the target array
$targets = @()
If ($vmware_tools_ids) {
foreach ($item in $reg_targets) {
$targets += $item + $vmware_tools_ids.reg_id
}
# Add the MSI installer ID regkey
$targets += "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{$($vmware_tools_ids.msi_id)}"
}
# This is a bit of a shotgun approach, but if we are at a version less than 2016, add the Uninstaller entries we don't
# try to automatically determine.
If ([Environment]::OSVersion.Version.Major -lt 10) {
$targets += "HKCR:\CLSID\{D86ADE52-C4D9-4B98-AA0D-9B0C7F1EBBC8}"
$targets += "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{9709436B-5A41-4946-8BE7-2AA433CAF108}"
$targets += "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{FE2F6A2C-196E-4210-9C04-2B1BC21F07EF}"
}
# Add the VMware, Inc regkey
If (Test-Path "HKLM:\SOFTWARE\VMware, Inc.") {
$targets += "HKLM:\SOFTWARE\VMware, Inc."
}
# Add the VMware Tools directory
If(Test-Path $VMware_Tools_Directory) {
$targets += $VMware_Tools_Directory
}
# Thanks to @Gadgetgeek2000 for pointing out that the script leaves some 500mb of extra artifacts on disk.
# This blob removes those.
If(Test-Path $VMware_Common_Directory) {
$targets += $VMware_Common_Directory
}
# Create a list of services to stop and remove
$services = Get-Service -DisplayName "VMware*"
$services += Get-Service -DisplayName "GISvc"
# Warn the user about what is about to happen
# Takes only y for an answer, bails otherwise.
Write-Host "The following registry keys, filesystem folders, and services will be deleted:"
If (!$targets -and !$services ) {
Write-Host "Nothing to do!"
}
Else {
$targets
$services
$user_confirmed = Read-Host "Continue (y/n)"
If ($user_confirmed -eq "y") {
# Stop all running VMware Services
$services | Stop-Service -Confirm:$false
# Cover for Remove-Service not existing in PowerShell versions < 6.0
If (Get-Command Remove-Service -errorAction SilentlyContinue) {
$services | Remove-Service -Confirm:$false
}
Else {
foreach ($s in $services) {
sc.exe DELETE $($s.Name)
}
}
# Remove all the files that are listed in $targets
foreach ($item in $targets) {
If(Test-Path $item) {
Remove-Item -Path $item -Recurse
}
}
Write-Host "Done. Reboot to complete removal."
}
Else {
Write-Host "Failed to get user confirmation"
}
}
@zwcloud
Copy link
Copy Markdown

zwcloud commented Sep 23, 2025

HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\VMware, Inc.

@Jason-Clark-FG
Copy link
Copy Markdown

Jason-Clark-FG commented Nov 10, 2025

With the help of AI assistance to do the typing, I tried to incorporate the additional suggestions here and also merge this forced removal with the modified msi removal from here: https://gist.github.com/KGHague/2c562ee88492c1c0c0eac1b3ae0fecd8. The script will first attempt to use the uninstaller, and then do the follow-up cleanup. I also tried to incorporate the ideas of removing the stray drivers as noted here: https://gist.github.com/KGHague/2c562ee88492c1c0c0eac1b3ae0fecd8?permalink_comment_id=5488837#gistcomment-5488837.

This has proved helpful in migrating from VMware to Proxmox VE, negating the need to remove the vmware-tools first which allows us to keep a functional "backup" copy in VMware until we are satisfied with the migration.

Here's the script:

<#
.SYNOPSIS
    Uninstalls and removes VMware Tools from a Windows system.
.DESCRIPTION
    This script first attempts to uninstall VMware Tools using the MSI installer method,
    even if the system is no longer running on VMware. It then performs a comprehensive
    cleanup by removing registry entries, filesystem folders, services, and devices.

    WARNING: If running this script on a system that has been migrated to a different
    hypervisor (e.g., Proxmox with VirtIO drivers), removing VMware storage drivers may
    cause boot failures. In such cases:
    - Ensure VirtIO drivers (or equivalent) are installed BEFORE running this script
    - If boot issues occur, change the disk controller type to IDE or SATA in the
      hypervisor settings, boot the system, then reinstall the appropriate drivers
    - Consider taking a snapshot before running this script if on a virtualized system

.PARAMETER Force
    Bypass the confirmation prompt and proceed with uninstall and cleanup automatically.
.PARAMETER Reboot
    Reboot the system after cleanup completes. If -Force is not specified, prompts for confirmation.
.EXAMPLE
    .\Cleanup-VMwareTools.ps1
    Prompts for confirmation before uninstalling and removing VMware Tools.
.EXAMPLE
    .\Cleanup-VMwareTools.ps1 -Force
    Uninstalls and removes VMware Tools without prompting for confirmation.
.EXAMPLE
    .\Cleanup-VMwareTools.ps1 -Force -Reboot
    Uninstalls and removes VMware Tools, then reboots automatically without prompting.
.EXAMPLE
    .\Cleanup-VMwareTools.ps1 -Reboot
    Uninstalls and removes VMware Tools with prompts, then asks before rebooting.
.NOTES
    This script combines techniques from two sources:
    - MSI uninstaller method: https://gist.github.com/KGHague/2c562ee88492c1c0c0eac1b3ae0fecd8
    - Brute-force cleanup method: https://gist.github.com/broestls/f872872a00acee2fca02017160840624
.LINK
    https://gist.github.com/KGHague/2c562ee88492c1c0c0eac1b3ae0fecd8
.LINK
    https://gist.github.com/broestls/f872872a00acee2fca02017160840624
#>

[CmdletBinding()]
Param (
    [Parameter(Mandatory=$false)]
    [switch]$Force,

    [Parameter(Mandatory=$false)]
    [switch]$Reboot
)

#Requires -RunAsAdministrator

#region Transcript and Logging Setup
# Start transcript with datestamped filename in script directory
$scriptName     = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name)
$transcriptPath = Join-Path $PSScriptRoot "${scriptName}_$(Get-Date -Format 'yyyyMMdd_HHmmss').log"
Start-Transcript -Path $transcriptPath -Append
Write-Host "Transcript started: $transcriptPath" -ForegroundColor Cyan
Write-Host "Script started at: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" -ForegroundColor Cyan

# Initialize script timer
$scriptStartTime = Get-Date
#endregion

#region Helper Functions
function Get-VMwareToolsInstallerID {
    <#
    .SYNOPSIS
        Retrieves the common ID used for VMware registry entries along with the MSI ID.
    .DESCRIPTION
        This function pulls out the common ID used for most of the VMware registry entries
        along with the ID associated with the MSI for VMware Tools.
    #>
    foreach ($item in $(Get-ChildItem Registry::HKEY_CLASSES_ROOT\Installer\Products)) {
        if ($item.GetValue('ProductName') -eq 'VMware Tools') {
            return @{
                reg_id = $item.PSChildName;
                msi_id = [Regex]::Match($item.GetValue('ProductIcon'), '(?<={)(.*?)(?=})') | Select-Object -ExpandProperty Value
            }
        }
    }
}
#endregion

#region Gather VMware Tools Information
$stepStartTime = Get-Date
Write-Host "`n=== Gathering VMware Tools Information ===" -ForegroundColor Cyan

# Get VMware Tools installer IDs before attempting uninstallation
# This ensures we have the registry IDs even if MSI uninstall removes them
$vmware_tools_ids = Get-VMwareToolsInstallerID

if ($vmware_tools_ids) {
    Write-Host "VMware Tools installer IDs found:" -ForegroundColor Green
    Write-Host "  Registry ID: $($vmware_tools_ids.reg_id)" -ForegroundColor Gray
    Write-Host "  MSI ID: $($vmware_tools_ids.msi_id)" -ForegroundColor Gray
}
else {
    Write-Host "VMware Tools installer IDs not found in registry." -ForegroundColor Yellow
}

$stepDuration = (Get-Date) - $stepStartTime
Write-Host "Step completed in $($stepDuration.TotalSeconds.ToString('F2')) seconds" -ForegroundColor Gray
#endregion

#region Step 1: MSI-based Uninstallation
$stepStartTime = Get-Date
Write-Host "`n=== Step 1: Attempting MSI-based Uninstallation ===" -ForegroundColor Cyan

# Create an instance of the WindowsInstaller.Installer object
$installer = New-Object -ComObject WindowsInstaller.Installer

# Use the packed GUID we already found earlier
if ($vmware_tools_ids) {
    Write-Host "VMware Tools installation found via registry." -ForegroundColor Green

    # Get the LocalPackage path from the registry using the packed GUID we already have
    $localPackage = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\$($vmware_tools_ids.reg_id)\InstallProperties" -ErrorAction SilentlyContinue | Select-Object -ExpandProperty LocalPackage

    if ($localPackage) {
        Write-Host "VMware Tools MSI path: $localPackage" -ForegroundColor Yellow

        # Open the MSI database in read-write mode
        $database = $installer.GetType().InvokeMember("OpenDatabase", "InvokeMethod", $null, $installer, @("${localPackage}", 2))

        # Remove the VM_LogStart and VM_CheckRequirements rows in the CustomAction table
        # VM_CheckRequirements added as recommended by @DanAvni
        $query = "DELETE FROM CustomAction WHERE Action='VM_LogStart' OR Action='VM_CheckRequirements'"
        $view  = $database.GetType().InvokeMember("OpenView", "InvokeMethod", $null, $database, @($query))
        $view.GetType().InvokeMember("Execute", "InvokeMethod", $null, $view, $null)
        $view.GetType().InvokeMember("Close", "InvokeMethod", $null, $view, $null)
        [void][System.Runtime.InteropServices.Marshal]::FinalReleaseComObject($view)

        # Commit the changes and close the database
        $database.GetType().InvokeMember("Commit", "InvokeMethod", $null, $database, $null)
        [void][System.Runtime.InteropServices.Marshal]::FinalReleaseComObject($database)

        Write-Host "MSI database modified successfully." -ForegroundColor Green

        # Check if Force parameter is used or get user confirmation
        if ($Force) {
            $user_confirmed = "y"
            Write-Host "Force parameter specified - proceeding with MSI uninstallation..." -ForegroundColor Yellow
        }
        else {
            $user_confirmed = Read-Host "Proceed with MSI uninstallation? (y/n)"
        }

        if ($user_confirmed -eq "y") {
            Write-Host "Uninstalling VMware Tools via MSI..." -ForegroundColor Yellow
            Start-Process msiexec.exe -ArgumentList "/x `"${localPackage}`" /qn /norestart" -Wait
            Write-Host "MSI uninstallation completed." -ForegroundColor Green
        }
        else {
            Write-Host "MSI uninstallation skipped by user." -ForegroundColor Yellow
        }
    }
    else {
        Write-Host "LocalPackage path not found in the registry." -ForegroundColor Yellow
    }
}
else {
    Write-Host "VMware Tools is not installed via MSI or not found in Win32_Product." -ForegroundColor Yellow
}

$stepDuration = (Get-Date) - $stepStartTime
Write-Host "Step completed in $($stepDuration.TotalSeconds.ToString('F2')) seconds" -ForegroundColor Gray
#endregion

#region Step 2: Comprehensive Cleanup
$stepStartTime = Get-Date
Write-Host "`n=== Step 2: Comprehensive Cleanup ===" -ForegroundColor Cyan

# Use the VMware Tools IDs gathered earlier
# Targets we can hit with the common registry ID from $vmware_tools_ids.reg_id
$reg_targets = @(
    "Registry::HKEY_CLASSES_ROOT\Installer\Features\",
    "Registry::HKEY_CLASSES_ROOT\Installer\Products\",
    "HKLM:\SOFTWARE\Classes\Installer\Features\",
    "HKLM:\SOFTWARE\Classes\Installer\Products\",
    "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\"
)

$VMware_Tools_Directory       = "${env:SystemDrive}\Program Files\VMware"
$VMware_Common_Directory      = "${env:SystemDrive}\Program Files\Common Files\VMware"
$VMware_Startmenu_Entry       = "${env:SystemDrive}\ProgramData\Microsoft\Windows\Start Menu\Programs\VMware\"
$VMware_ProgramData_Directory = "${env:SystemDrive}\ProgramData\VMware"

# Create an empty array to hold all the uninstallation targets and compose the entries into the target array
$targets = @()

if ($vmware_tools_ids) {
    foreach ($item in $reg_targets) {
        $targets += $item + $vmware_tools_ids.reg_id
    }
    # Add the MSI installer ID regkey
    $targets += "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{$($vmware_tools_ids.msi_id)}"
}

# This is a bit of a shotgun approach, but if we are at a version less than 2016, add the Uninstaller entries we don't
# try to automatically determine.
if ([Environment]::OSVersion.Version.Major -lt 10) {
    $targets += "HKCR:\CLSID\{D86ADE52-C4D9-4B98-AA0D-9B0C7F1EBBC8}"
    $targets += "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{9709436B-5A41-4946-8BE7-2AA433CAF108}"
    $targets += "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{FE2F6A2C-196E-4210-9C04-2B1BC21F07EF}"
}

# Add the VMware, Inc regkey
if (Test-Path "HKLM:\SOFTWARE\VMware, Inc.") {
    $targets += "HKLM:\SOFTWARE\VMware, Inc."
}
if (Test-Path "HKLM:\SOFTWARE\WOW6432Node\VMware, Inc.") {
    $targets += "HKLM:\SOFTWARE\WOW6432Node\VMware, Inc."
}

# Add the VMware User Process run key value
$runKeyPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run"
if (Test-Path $runKeyPath) {
    $runKey = Get-ItemProperty -Path $runKeyPath -ErrorAction SilentlyContinue
    if ($runKey."VMware User Process") {
        # Store the registry path with value name for later deletion
        $targets += "$runKeyPath|VMware User Process"
    }
}

# Add the VMware Tools directory
if (Test-Path $VMware_Tools_Directory) {
    $targets += $VMware_Tools_Directory
}

# Thanks to @Gadgetgeek2000 for pointing out that the script leaves some 500mb of extra artifacts on disk.
# This blob removes those.
if (Test-Path $VMware_Common_Directory) {
    $targets += $VMware_Common_Directory
}

if (Test-Path $VMware_Startmenu_Entry) {
    $targets += $VMware_Startmenu_Entry
}

if (Test-Path $VMware_ProgramData_Directory) {
    $targets += $VMware_ProgramData_Directory
}

# Create a list of services to stop and remove
$services = Get-Service -DisplayName "VMware*" -ErrorAction SilentlyContinue
$services += Get-Service -DisplayName "GISvc" -ErrorAction SilentlyContinue

# Create list of VMware devices to remove
$vmwareDevices = Get-PnpDevice | Where-Object { $_.FriendlyName -like "*VMware*" }

# Warn the user about what is about to happen
if (!$targets -and !$services) {
    Write-Host "No cleanup targets found. Nothing to do!" -ForegroundColor Green
}
else {
    Write-Host "`nThe following registry keys, filesystem folders, services and devices will be deleted:" -ForegroundColor Yellow
    if ($targets) {
        Write-Host "`nTargets:" -ForegroundColor Yellow
        $targets | ForEach-Object { Write-Host "  - $_" }
    }
    if ($services) {
        Write-Host "`nServices:" -ForegroundColor Yellow
        $services | ForEach-Object { Write-Host "  - $($_.Name) ($($_.DisplayName))" }
    }
    if ($vmwareDevices) {
        Write-Host "`nDevices:" -ForegroundColor Yellow
        $vmwareDevices | ForEach-Object { Write-Host "  - $($_.FriendlyName) [$($_.InstanceId)]" }
    }

    # Check if Force parameter is used or get user confirmation
    if ($Force) {
        $cleanup_confirmed = "y"
        Write-Host "`nForce parameter specified - proceeding with cleanup without confirmation..." -ForegroundColor Yellow
    }
    else {
        $cleanup_confirmed = Read-Host "`nContinue with cleanup? (y/n)"
    }

    $global:ErrorActionPreference = 'SilentlyContinue'
    if ($cleanup_confirmed -eq "y") {
        # if vmStatsProvider.dll exists, unregister it first
        $vmStatsProvider = "c:\Program Files\VMware\VMware Tools\vmStatsProvider\win64\vmStatsProvider.dll"
        if (Test-Path $vmStatsProvider) {
            Write-Host "Unregistering vmStatsProvider.dll..." -ForegroundColor Yellow
            Regsvr32 /s /u $vmStatsProvider
        }

        # Stop all running VMware Services
        Write-Host "Stopping VMware services..." -ForegroundColor Yellow
        $services | Stop-Service -Confirm:$false -ErrorAction SilentlyContinue

        # Cover for Remove-Service not existing in PowerShell versions < 6.0
        Write-Host "Removing VMware services..." -ForegroundColor Yellow
        if (Get-Command Remove-Service -ErrorAction SilentlyContinue) {
            $services | Remove-Service -Confirm:$false -ErrorAction SilentlyContinue
        }
        else {
            foreach ($s in $services) {
                sc.exe DELETE $($s.Name)
            }
        }

        # Stop dependent services to unlock files
        Write-Host "Stopping dependent services temporarily..." -ForegroundColor Yellow
        $dep = Get-Service -Name "EventLog" -DependentServices | Select-Object -Property Name
        Stop-Service -Name "EventLog" -Force -ErrorAction SilentlyContinue
        Stop-Service -Name "wmiApSrv" -Force -ErrorAction SilentlyContinue
        $dep += Get-Service -Name "winmgmt" -DependentServices | Select-Object -Property Name
        Stop-Service -Name "winmgmt" -Force -ErrorAction SilentlyContinue
        Start-Sleep -Seconds 5

        # Remove all the files that are listed in $targets
        Write-Host "Removing registry keys, registry values, and filesystem folders..." -ForegroundColor Yellow
        foreach ($item in $targets) {
            # Check if this is a registry value (denoted by pipe separator)
            if ($item -match '^(.+)\|(.+)$') {
                $regPath = $Matches[1]
                $valueName = $Matches[2]
                if (Test-Path $regPath) {
                    Write-Verbose "Removing registry value: $valueName from $regPath"
                    Remove-ItemProperty -Path $regPath -Name $valueName -Force -ErrorAction SilentlyContinue
                }
            }
            elseif (Test-Path $item) {
                Write-Verbose "Removing: $item"
                Get-Childitem -Path $item -Recurse | Remove-Item -Force -Recurse -ErrorAction SilentlyContinue
                Remove-Item -Path $item -Recurse -Force -ErrorAction SilentlyContinue
            }
        }

        # Restart dependent services
        Write-Host "Restarting dependent services..." -ForegroundColor Yellow
        Start-Service -Name "EventLog" -ErrorAction SilentlyContinue
        Start-Service -Name "wmiApSrv" -ErrorAction SilentlyContinue
        Start-Service -Name "winmgmt" -ErrorAction SilentlyContinue
        foreach ($service in $dep) {
            Start-Service $service.Name -ErrorAction SilentlyContinue
        }

        # Remove VMware devices
        if ($vmwareDevices.Count -gt 0) {
            Write-Host "Removing VMware devices..." -ForegroundColor Yellow
            foreach ($device in $vmwareDevices) {
                Write-Verbose "Removing device: $($device.FriendlyName) [$($device.InstanceId)]"
                pnputil /remove-device $device.InstanceId 2>&1 | Out-Null
            }
        }
        else {
            Write-Host "No VMware devices found." -ForegroundColor Green
        }

        # Remove VMware driver packages from the driver store
        Write-Host "Removing VMware driver packages..." -ForegroundColor Yellow
        $pnpOutput = pnputil /enum-drivers
        $vmwareDrivers = @()

        for ($i = 0; $i -lt $pnpOutput.Count; $i++) {
            if ($pnpOutput[$i] -match "Published Name\s*:\s*(oem\d+\.inf)") {
                $oemInf = $Matches[1]
                # Check the next few lines for VMware in the original or provider name
                $driverBlock = $pnpOutput[$i..($i+5)] -join " "
                if ($driverBlock -match "VMware") {
                    $vmwareDrivers += $oemInf
                }
            }
        }

        if ($vmwareDrivers.Count -gt 0) {
            Write-Host "Found $($vmwareDrivers.Count) VMware driver package(s) in driver store" -ForegroundColor Yellow
            foreach ($driver in $vmwareDrivers) {
                Write-Verbose "Deleting driver package: $driver"
                pnputil /delete-driver $driver /uninstall /force 2>&1 | Out-Null
            }
        }
        else {
            Write-Host "No VMware driver packages found in driver store." -ForegroundColor Green
        }

        Start-Sleep -Seconds 5

        $stepDuration = (Get-Date) - $stepStartTime
        Write-Host "Step completed in $($stepDuration.TotalSeconds.ToString('F2')) seconds" -ForegroundColor Gray

        Write-Host "`n=== Cleanup Complete ===" -ForegroundColor Green
        Write-Host "Please reboot the system to complete VMware Tools removal." -ForegroundColor Yellow
    }
    else {
        Write-Host "Cleanup cancelled by user." -ForegroundColor Red
        $stepDuration = (Get-Date) - $stepStartTime
        Write-Host "Step completed in $($stepDuration.TotalSeconds.ToString('F2')) seconds" -ForegroundColor Gray
    }
}
#endregion

#region Finalize
$scriptDuration = (Get-Date) - $scriptStartTime
Write-Host "`n========================================" -ForegroundColor Cyan
Write-Host "Total script execution time: $($scriptDuration.TotalSeconds.ToString('F2')) seconds ($($scriptDuration.ToString('mm\:ss')))" -ForegroundColor Cyan
Write-Host "Script completed at: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" -ForegroundColor Cyan
Write-Host "Transcript saved to: $transcriptPath" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan

Stop-Transcript

# Handle reboot if requested
if ($Reboot) {
    if ($Force) {
        Write-Host "`nRebooting system now..." -ForegroundColor Yellow
        Restart-Computer -Force
    }
    else {
        $rebootConfirmed = Read-Host "`nReboot now? (y/n)"
        if ($rebootConfirmed -eq "y") {
            Write-Host "Rebooting system now..." -ForegroundColor Yellow
            Restart-Computer -Force
        }
        else {
            Write-Host "Reboot cancelled. Please reboot manually to complete removal." -ForegroundColor Yellow
        }
    }
}
#endregion

Additional suggestions and input are welcome.

@luntrick
Copy link
Copy Markdown

luntrick commented Jan 4, 2026

Thanks to everyone for the iterative dialogue that made this script so successful.

@pirklb
Copy link
Copy Markdown

pirklb commented Mar 13, 2026

@Jason-Clark-FG Thank's, your script works fine (moving to HPE VME Essentials) on Windows Server 2025 (no VM* Service, no C:\ProgramData\VMware Folder, in the registry there are still some entries containing "VMware" e, g. in Classes which point to C:\Program Files\VMware\VMware Tools und C:\Program Files\Common Files\VMware - but I don't think these will cause a Problem in the future). The only VMware I find on the system partition is in C:\Windows\SoftwareDistribution\Download (which is the Cache for the Update service, so I see no problem with this)

@EPiSKiNG
Copy link
Copy Markdown

@Jason-Clark-FG THANK YOU SO MUCH! Really appreciate that script! Super helpful for my migrations!

@Jason-Clark-FG
Copy link
Copy Markdown

@Jason-Clark-FG Thank's, your script works fine (moving to HPE VME Essentials) on Windows Server 2025 (no VM* Service, no C:\ProgramData\VMware Folder, in the registry there are still some entries containing "VMware" e, g. in Classes which point to C:\Program Files\VMware\VMware Tools und C:\Program Files\Common Files\VMware - but I don't think these will cause a Problem in the future). The only VMware I find on the system partition is in C:\Windows\SoftwareDistribution\Download (which is the Cache for the Update service, so I see no problem with this)

Thanks @pirklb, so is there anything you think we should add to the script? We currently do not have an Windows Server 2025 deployed, so I cannot take a look around in one.

@Jason-Clark-FG
Copy link
Copy Markdown

@Jason-Clark-FG THANK YOU SO MUCH! Really appreciate that script! Super helpful for my migrations!

Glad to hear it @EPiSKiNG, it's been super helpful for us too. I just streamlined a few things I found in various places. Standing on the shoulders of giants as they say :-)

@pirklb
Copy link
Copy Markdown

pirklb commented Apr 1, 2026

@Jason-Clark-FG Thank's, your script works fine (moving to HPE VME Essentials) on Windows Server 2025 (no VM* Service, no C:\ProgramData\VMware Folder, in the registry there are still some entries containing "VMware" e, g. in Classes which point to C:\Program Files\VMware\VMware Tools und C:\Program Files\Common Files\VMware - but I don't think these will cause a Problem in the future). The only VMware I find on the system partition is in C:\Windows\SoftwareDistribution\Download (which is the Cache for the Update service, so I see no problem with this)

Thanks @pirklb, so is there anything you think we should add to the script? We currently do not have an Windows Server 2025 deployed, so I cannot take a look around in one.

Hello @Jason-Clark-FG - I think it is fine (we are currently in a poc to see, if we can switch from VMware to HPE VME. And for the poc it works "well". If we decide to move - and have to migrate more VMs - maybe I'll find some improvements for the script. If so, I will share it.

@Car10sH
Copy link
Copy Markdown

Car10sH commented Apr 24, 2026

I've tried making both of the modifications to the MSI suggested in the script (removed VM_LogStart and VM_CheckRequirements from CustomAction table), but the uninstallation fails (msiexec crash in Application event log). This worked on the previous version of VMware Tools we were using (12.5.2), but no longer works on a newer version (12.5.4). Is this method no longer viable? If so, happy to rely on the script you've provided, but it would be nice if the uninstall process worked as well. Any advice appreciated, thank you.

@mateuszdrab
Copy link
Copy Markdown

I've tried making both of the modifications to the MSI suggested in the script (removed VM_LogStart and VM_CheckRequirements from CustomAction table), but the uninstallation fails (msiexec crash in Application event log). This worked on the previous version of VMware Tools we were using (12.5.2), but no longer works on a newer version (12.5.4). Is this method no longer viable? If so, happy to rely on the script you've provided, but it would be nice if the uninstall process worked as well. Any advice appreciated, thank you.

I can only suggest that you post the details of the crash from the event log as well as running the uninstall through msiexec with verbose logging output to see where and why it crashes.

Without that, I won't be able to help, I haven't had VMware for a while.

@Car10sH
Copy link
Copy Markdown

Car10sH commented Apr 24, 2026

Thanks @mateuszdrab - I will do that.

@Car10sH
Copy link
Copy Markdown

Car10sH commented Apr 29, 2026

@mateuszdrab - I found the cause of the failure, it was CustomAction "VM_UninstGHIRestGuestHandle.869A7E00_8665_0000_83A8_EF0F76CF0001". I removed this via Orca (along with VM_CheckRequirements, didn't need to remove VM_LogStart however) and uninstall completed successfully. This affects version 12.5.4, internal version 12452, build 24964629, VM tested against is running in Azure native. I'm unable to confirm if the last part of the CustomAction name changes in other versions as this is the only version we are running in our VMware environment at present.

@SDC-SYSAD
Copy link
Copy Markdown

Just wanted to add, the Orca method worked perfectly for our 30ish VMs we bothered to migrate from VMWare to Hyper-V! Thanks for the tip!

@ItMan47
Copy link
Copy Markdown

ItMan47 commented May 15, 2026

@Car10sH and @SDC-SYSAD
Hi could do with some help, can you drop me an idiots guide on the steps you used to do the Orca method please?

The script used method DELETE FROM ... WHERE... which looks like standard SQL
Orca options seem to use a different syntax and I am guessing Drop Row = DELETE but I could be wrong.

@SDC-SYSAD
Copy link
Copy Markdown

SDC-SYSAD commented May 15, 2026

@Car10sH and @SDC-SYSAD Hi could do with some help, can you drop me an idiots guide on the steps you used to do the Orca method please?

I just did this:
-Find MSI in C:\Windows\Installer
-Open MSI with Orca
-CTRL + F to find VM_LogStart
-There were 3 entries in the MSI, find all 3
-Right click, Drop Row
-Save

EDIT - After editing the MSI I then use the standard Windows Settings > Apps > Uninstall on the VMWare Tools app.

That worked for me and I just rinsed and repeated all on my VMs.

@ItMan47
Copy link
Copy Markdown

ItMan47 commented May 15, 2026 via email

@SDC-SYSAD
Copy link
Copy Markdown

Thank you. Just edited my post to highlight the source of my confusion. I am seeing a complication in Car10sH post because I am on the affected tools version ☹ So I may drop that row also. Kind Regards, Nick Nicholas Kulkarni. MInstLM. Dip.Comp(open) IT Manager 5 Star Cases Limited From: SDC-SYSAD @.> Sent: Friday, May 15, 2026 3:28 PM To: SDC-SYSAD @.> Cc: Comment @.> Subject: Re: broestls/Remove_VMwareTools.ps1 You don't often get email from @.@.***>. Learn why this is importanthttps://aka.ms/LearnAboutSenderIdentification CAUTION: This email originated from outside of the organization. Do not click links or open attachments unless you recognize the sender and know the content is safe. @SDC-SYSAD commented on this gist.

________________________________ @Car10sHhttps://github.com/Car10sH and @SDC-SYSADhttps://github.com/SDC-SYSAD Hi could do with some help, can you drop me an idiots guide on the steps you used to do the Orca method please? I just did this: -Find MSI in C:\Windows\Installer -Open MSI with Orca -CTRL + F to find VM_LogStart -There were 3 entries in the MSI, find all 3 -Right click, Drop Row -Save That worked for me and I just rinsed and repeated all on my VMs. — Reply to this email directly, view it on GitHubhttps://gist.github.com/broestls/f872872a00acee2fca02017160840624#gistcomment-6151828 or unsubscribehttps://github.com/notifications/unsubscribe-auth/AXXETLHNTZ6COPSVH3JV5FT424SPBBFHORZGSZ3HMVZKMY3SMVQXIZNMON2WE2TFMN2F65DZOBS2WR3JON2EG33NNVSW45FGORXXA2LDOOIYFJDUPFYGLJDHNFZXJJLWMFWHKZNJGEYDONZXHE2TCMFKMF2HI4TJMJ2XIZLTSOBKK5TBNR2WLKJSHA2DQOJQGI2DDJDOMFWWLKDBMN2G64S7NFSIFJLWMFWHKZNEORZHKZNENZQW2ZN3ORUHEZLBMRPXAYLSORUWG2LQMFXHIX3BMN2GS5TJOR4YFJLWMFWHKZNEM5UXG5FENZQW2ZNLORUHEZLBMRPXI6LQMU. You are receiving this email because you commented on the thread. Triage notifications on the go with GitHub Mobile for iOShttps://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Androidhttps://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.

Sorry forgot to say, I didn't use any scripts to get rid of it. I just used the standard uninstall method within Windows, which only works once you've removed those 3 entries from the MSI.

@ItMan47
Copy link
Copy Markdown

ItMan47 commented May 15, 2026

Thanks @SDC-SYSAD , much appreciated I also dropped three lines with reference to @Car10sH post. About to try this in anger. Will let you know :)

@ItMan47
Copy link
Copy Markdown

ItMan47 commented May 15, 2026

@SDC-SYSAD
Screenshot 2026-05-15 154713
Still stuck with the above.
Didn't work. I am going to try without the modification suggested by @Car10sH and see what happens

@SDC-SYSAD
Copy link
Copy Markdown

:( That's frustrating! I wish you luck! :-)

@ItMan47
Copy link
Copy Markdown

ItMan47 commented May 15, 2026

@SDC-SYSAD no luck, crashes out just as @Car10sH says. It seems to be a problem with that particular version. 12.5.4.249629. I am going to have to try the script.

@Car10sH
Copy link
Copy Markdown

Car10sH commented May 15, 2026

@ItMan47 - As suggested by mateuszdrab, it is worth you running the msiexec uninstall process with verbose logging enabled, as this will help you to identify what is causing the uninstall process to fail.

  • From elevated Command Prompt, navigate to C:\Windows\Installer
  • Locate the VMware Tools MSI (usually has a hexadecimal value for a filename)
  • Run: msiexec /x <msifilename.msi> or {product_code} /L*v C:\Temp<logname.log>
  • Once the installer fails, open the log file and search for "1603" - this should give you a pointer towards the problem (that's how I found the reference to the CustomAction: VM_UninstGHIRestGuestHandle.869A7E00_8665_0000_83A8_EF0F76CF0001)

You can then remove references to whichever CustomAction it shows from the MSI via Orca.

Hope this helps!

@ItMan47
Copy link
Copy Markdown

ItMan47 commented May 15, 2026 via email

@ItMan47
Copy link
Copy Markdown

ItMan47 commented May 15, 2026

@Car10sH Interesting, I thought I had removed all instances of VM_CheckRequirements with Orca but I am seeing this
MSI (s) (B4:C4) [16:54:03:068]: Doing action: VM_CheckRequirements
Action ended 16:54:03: LaunchConditions. Return value 1.
MSI (s) (B4:B0) [16:54:03:083]: Invoking remote custom action. DLL: C:\Windows\Installer\MSIAF20.tmp, Entrypoint: VMCheckRequirements
Action start 16:54:03: VM_CheckRequirements.
CustomAction VM_CheckRequirements returned actual error code 1603 (note this may not be 100% accurate if translation happened inside sandbox)
MSI (s) (B4:C4) [16:54:03:630]: Doing action: VM_SendMsiLogToHostOnError
Action ended 16:54:03: VM_CheckRequirements. Return value 3.

Double checking the MSI now.

@ItMan47
Copy link
Copy Markdown

ItMan47 commented May 15, 2026

CTRL+F VM_CheckRequirements returns nothing found. I am stumped.
did find this also just after that 1603

MSI (s) (B4:98) [16:54:03:646]: Invoking remote custom action. DLL: C:\Windows\Installer\MSIB154.tmp, Entrypoint: VMSendMsiLogToHost
Action start 16:54:03: VM_SendMsiLogToHostOnError.
Action ended 16:54:03: VM_SendMsiLogToHostOnError. Return value 1.
Action ended 16:54:03: INSTALL. Return value 3.

Clearly that isn't going to work either.

@Car10sH
Copy link
Copy Markdown

Car10sH commented May 15, 2026

@ItMan47 Are you modifying the relevant MSI held in C:\Windows\Installer via Orca? I found it only works if you update that one (make a copy first just in case, give it a .bak extension or something), as that is what the uninstall process calls.

@ItMan47
Copy link
Copy Markdown

ItMan47 commented May 15, 2026

@Car10sH kind of, I copied this out of the folder on to another machine, edited it with orca and saved it, then copied the edited file I saved back over to the original folder.

@ItMan47
Copy link
Copy Markdown

ItMan47 commented May 15, 2026

@Car10sH that path you gave me C:\Windows\Installer is that correct? The VM is running a Server OS not a desktop OS. I am finding the file is in C:\Program Files\Common Files\VMware\InstallerCache. Just running a search for the Product Code to see if it is anywhere else on the machine. I found a folder with the product code in the location you gave but it only has the Icon file in it. I am going to copy the modified msi in there and see if it will execute.

@ItMan47
Copy link
Copy Markdown

ItMan47 commented May 15, 2026

No joy, calling it a night, hit this again on Monday, thanks to @Car10sH and @SDC-SYSAD

@ItMan47
Copy link
Copy Markdown

ItMan47 commented May 15, 2026

https://help.zerto.com/bundle/z-kb-articles-zertokbs/page/4336.html
_When you try to install VMware tools on a VM that's running on Hyper-V hypervisor, the installation fails.
The reason is that the VMware Tools installer checks for an underlying VMware product hosting the VM. If it isn't found, the installer won't continue. VMware tools can't be installed on a VM that's running on Hyper-V hypervisor.
The correct procedure to install the VMware tools on a VM that's running on Hyper-V is to failover the VM to the VMware environment, install the VMware tools, and then failback.

Workaround
If you want to work around the correct procedure, you can download a VMware Tools MSI and remove the table row that runs this check. If you currently have an MSI for VMware Tools, open it for editing (ORCA is a commonly used tool for this - ORCA.EXE), and remove the row “VM_CheckRequirements” from the sequence table “InstallUISequence.”_

Having removed every instance I could find with Orca I suspect the VMCheckRequirements has been hard coded into the version I have at low level. It was one of the latest released by VMWare (its a patch for a CVE) and this is long after Broadcom's takeover. That would explain the log file still calling VMCheckRequirements even after applying the workaround. This would be designed to defeat the workaround and may have broken the uninstall too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment