🚀 Overview
Add/Remove Programs says VMware Tools isn't installed. The MSI uninstaller can't find a
product to remove. But C:\Program Files\VMware is still sitting there, a handful of
VMware* services are still listed in services.msc, and the registry is full of
orphaned Installer\Products entries. This usually happens after a botched upgrade, a Tools
package that was removed by deleting files instead of uninstalling properly, or — most commonly — a VM
that's being prepared for a move off VMware entirely.
This script does the manual job an engineer would otherwise do by hand: find every trace VMware Tools left in the registry and on disk, show you exactly what it found, and — once you confirm — remove all of it cleanly. It's written for Windows Server 2008 R2 through 2019 and has been tested directly on 2016 and 2019.
Programs and Features or the official VMware uninstaller instead.
🔍 Why the Standard Uninstaller Fails
A handful of recurring causes account for most "VMware Tools won't uninstall" situations on generic Windows Server builds:
- The MSI's ProductCode registration in
HKEY_CLASSES_ROOT\Installer\Productssurvived a previous failed uninstall, but the cached MSI package it points to is gone — so Windows Installer has nothing to run. - Tools was removed by deleting
C:\Program Files\VMwaredirectly rather than through the uninstaller, leaving the registry registration completely orphaned. - The VM was P2V'd, cloned, or template-deployed from a source where Tools was installed differently, leaving installer metadata that doesn't match the running OS.
- A repeated upgrade-then-downgrade cycle left more than one orphaned
"VMware Tools"product entry in the registry — the second one is invisible toPrograms and Featuresbut still blocks a clean reinstall.
In every one of these cases, the fix isn't "uninstall again" — there's nothing left for the uninstaller to act on. It's a manual cleanup of the leftovers.
🗂️ What Gets Cleaned Up
The script builds its target list dynamically rather than hardcoding paths, so it adapts to whatever installer IDs are actually present on the box in front of it.
| Target | Why it's there |
|---|---|
HKCR\Installer\Products\<ID>HKCR\Installer\Features\<ID> | The MSI's core product/feature registration — found automatically by matching ProductName = 'VMware Tools'. |
HKLM\...\Uninstall\{MSI ProductCode} | The entry that drives the Programs and Features list — extracted from the matching product's ProductIcon value. |
HKLM\...\Installer\UserData\<every SID>\Products\<ID> | Per-user install registration. Walked across every SID under UserData, not just SYSTEM — Tools can be registered under whichever account ran the original install. |
HKLM\SOFTWARE\VMware, Inc. | Shared configuration hive used by Tools and other VMware components. |
C:\Program Files\VMwareC:\Program Files\Common Files\VMwareC:\ProgramData\VMware | Binaries, shared components, and logs/config — ProgramData alone can hold several hundred MB of old log data. |
| Legacy CLSID / Uninstall GUIDs | A small set of fixed entries only added on Windows builds older than Server 2016, where auto-detection is less reliable. |
Services matching VMware*, vm*, or named GISvc | Covers components whose display name doesn't start with "VMware" — the Guest Info service being the classic example. |
✅ Prerequisites & Safety Checklist
Run elevated
The script checks for Administrator rights on launch and exits cleanly if it isn't running elevated — registry and Program Files access both require it.
Snapshot or back up first
Take a VM snapshot (or a file-level backup if it's already off VMware) before running this on anything you can't easily rebuild.
Test on one non-critical server first
Run it interactively on a single, low-priority VM and read the printed target list before rolling it out to anything that matters.
PowerShell 5.1 or later
Works on Windows PowerShell 5.1 (built into Server 2016/2019) and PowerShell 7.x. Falls back to sc.exe for service removal if Remove-Service isn't available.
📜 How the Script Works
The flow is linear and deliberately simple to follow — discover, build, filter, confirm, remove:
1. Discovering every orphaned installer entry
Rather than assuming there's exactly one "VMware Tools" registration, the script enumerates
Installer\Products and collects every match — important because repeated
upgrade/downgrade cycles can leave more than one behind:
function Get-VMwareToolsInstallerIDs {
$found = @()
Get-ChildItem Registry::HKEY_CLASSES_ROOT\Installer\Products -ErrorAction SilentlyContinue | ForEach-Object {
if ($_.GetValue('ProductName') -eq 'VMware Tools') {
$icon = $_.GetValue('ProductIcon')
$msiId = $null
if ($icon) {
$m = [Regex]::Match($icon, '(?<=\{)(.*?)(?=\})')
if ($m.Success) { $msiId = $m.Value }
}
$found += [PSCustomObject]@{ reg_id = $_.PSChildName; msi_id = $msiId }
}
}
return $found
}
The MSI ProductCode (used to find the matching Uninstall registry key)
is pulled out of the ProductIcon value, which embeds it between curly braces.
2. Walking every SID under UserData
This is the part that's easy to get wrong: the per-user MSI registration under UserData isn't
always filed under the SYSTEM SID. If Tools was ever installed interactively by an administrator account,
the registration sits under that account's SID instead. The script walks all of them:
$userDataRoot = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData"
if (Test-Path $userDataRoot) {
Get-ChildItem $userDataRoot -ErrorAction SilentlyContinue | ForEach-Object {
$targets += (Join-Path $_.PSPath "Products\$($entry.reg_id)")
}
}
3. Filtering before the confirmation prompt
Every computed path is checked with Test-Path before it's shown to you, so the
list you confirm against matches exactly what will be touched — no theoretical paths that don't actually
exist on this particular server.
4. The confirmation gate (or -Force for automation)
$proceed = $Force.IsPresent
if (-not $proceed) {
$proceed = (Read-Host "Continue (y/n)") -eq 'y'
}
Run it with no parameters and you get the interactive prompt. Add -Force and it skips
straight to removal — what you want when this is driven remotely across several servers rather than
typed at a console one at a time.
🖥️ Running It
Interactive — single server
# In an elevated PowerShell session on the target server
.\Remove-VMwareTools.ps1
Read the printed list of registry keys, folders, and services carefully, then type y to proceed.
Unattended — for remote or scripted runs
.\Remove-VMwareTools.ps1 -Force
Across multiple servers with PowerShell remoting
Once you've validated the behaviour interactively on a test box, the same script can be pushed out to a
batch of generic servers via Invoke-Command:
$targetServers = @('SERVER01', 'SERVER02', 'SERVER03')
$cred = Get-Credential # local or domain admin on the targets
Invoke-Command -ComputerName $targetServers -Credential $cred -ScriptBlock {
& 'C:\Scripts\Remove-VMwareTools.ps1' -Force
}
Invoke-Command -ScriptBlock runs against a local path on the remote machine, it doesn't transfer the file for you.
🚫 What It Doesn't Touch
To keep the script predictable, it deliberately stays out of two areas:
- Kernel driver binaries under
C:\Windows\System32\drivers(the VMware network, SCSI, and display drivers) are left in place. They're harmless once the VM is running on different virtual hardware — Windows simply won't load a driver for a device that isn't present. - Driver Store packages (visible via
pnputil /enum-drivers) are also left alone, for the same reason.
If you want a full hygiene check after migrating off VMware, this is a quick generic sanity check:
driverquery /v | findstr /i vmware
💡 Pro Tips & Best Practices
Snapshot before you run it
Cheap insurance — take it immediately before, not "earlier today".
Pilot on one box per OS version
2008 R2/2012 R2 hit the legacy GUID block; 2016/2019 don't. Test both paths before a fleet-wide run.
Reboot before reinstalling
Some services and locked files only fully release after a restart — reboot before attempting a fresh VMware Tools install or hypervisor migration step.
Keep the console output
Redirect the printed target list to a log file per server (... | Tee-Object) if you're running this across a batch — useful evidence if anything needs to be restored.
📦 GitHub Repository
The full script — Remove-VMwaretools.ps1 — is published in the Infra Automation repository, ready to drop onto a Windows agent or run directly on a target server.
View Remove-VMwaretools.ps1 on GitHub