Last active
November 26, 2025 15:30
-
-
Save Jason-Clark-FG/e2feca01b6d0696e4342f081fb33c8fd to your computer and use it in GitHub Desktop.
A quick refomatting of the gist at https://gist.github.com/broestls/f872872a00acee2fca02017160840624 with the additional suggestions incorporated
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <# | |
| .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://gist.github.com/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