Last active
November 26, 2025 15:31
-
-
Save Jason-Clark-FG/68e23b534f5be4e3f3b43f3bd37f1d65 to your computer and use it in GitHub Desktop.
A quick update to the gist at https://gist.github.com/KGHague/2c562ee88492c1c0c0eac1b3ae0fecd8 to incorporate the additional suggestions
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 | |
| Uninstalls VMware Tools using the MSI installer method. | |
| .DESCRIPTION | |
| This script modifies the VMware Tools MSI database to bypass environment checks | |
| that prevent uninstallation on non-VMware systems, then performs an uninstall. | |
| .PARAMETER Uninstall | |
| Actually perform the uninstall. Without this switch, the script runs in dry-run mode. | |
| .PARAMETER Force | |
| Bypass the confirmation prompt and proceed with uninstallation automatically. | |
| .EXAMPLE | |
| .\Uninstall-VMwareTools.ps1 | |
| Dry run mode - shows what would be done without actually uninstalling. | |
| .EXAMPLE | |
| .\Uninstall-VMwareTools.ps1 -Uninstall | |
| Prompts for confirmation before uninstalling VMware Tools. | |
| .EXAMPLE | |
| .\Uninstall-VMwareTools.ps1 -Uninstall -Force | |
| Uninstalls VMware Tools without prompting for confirmation. | |
| .LINK | |
| https://gist.github.com/KGHague/2c562ee88492c1c0c0eac1b3ae0fecd8 | |
| #> | |
| [CmdletBinding()] | |
| Param ( | |
| [Parameter(Mandatory=$false)] | |
| [switch]$Uninstall = $false, | |
| [Parameter(Mandatory=$false)] | |
| [switch]$Force | |
| ) | |
| #Requires -RunAsAdministrator | |
| function ConvertTo-PackedGuid { | |
| <# | |
| .SYNOPSIS | |
| https://gist.github.com/MyITGuy/d3e039c5ec7865edefc157fcd625a20a | |
| Converts a GUID string into a packed globally unique identifier (GUID) string. | |
| .DESCRIPTION | |
| Takes a GUID string and breaks it into 6 parts. It then loops through the first five parts and reversing the order. It loops through the sixth part and reversing the order of every 2 characters. It then joins the parts back together and returns a packed GUID string. | |
| .EXAMPLE | |
| ConvertTo-PackedGuid -Guid '{7C6F0282-3DCD-4A80-95AC-BB298E821C44}' | |
| The output of this example would be: 2820F6C7DCD308A459CABB92E828C144 | |
| .PARAMETER Guid | |
| A globally unique identifier (GUID). | |
| #> | |
| [CmdletBinding()] | |
| [OutputType([System.String])] | |
| param ( | |
| [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, Mandatory)] | |
| [ValidateScript( { [System.Guid]::Parse($_) -is [System.Guid] })] | |
| [System.Guid]$Guid | |
| ) | |
| process { | |
| Write-Verbose "Guid: $($Guid)" | |
| $GuidString = $Guid.ToString('N') | |
| Write-Verbose "GuidString: $($GuidString)" | |
| $Indexes = [ordered]@{ | |
| 0 = 8 | |
| 8 = 4 | |
| 12 = 4 | |
| 16 = 2 | |
| 18 = 2 | |
| 20 = 12 | |
| } | |
| $PackedGuid = '' | |
| foreach ($index in $Indexes.GetEnumerator()) { | |
| $Substring = $GuidString.Substring($index.Key, $index.Value) | |
| Write-Verbose "Substring: $($Substring)" | |
| switch ($index.Key) { | |
| 20 { | |
| $parts = $Substring -split '(.{2})' | Where-Object { $_ } | |
| foreach ($part In $parts) { | |
| $part = $part -split '(.{1})' | |
| Write-Verbose "Part: $($part)" | |
| [System.Array]::Reverse($part) | |
| Write-Verbose "Reversed: $($part)" | |
| $PackedGuid += $part -join '' | |
| } | |
| } | |
| default { | |
| $part = $Substring.ToCharArray() | |
| Write-Verbose "Part: $($part)" | |
| [System.Array]::Reverse($part) | |
| Write-Verbose "Reversed: $($part)" | |
| $PackedGuid += $part -join '' | |
| } | |
| } | |
| } | |
| [System.Guid]::Parse($PackedGuid).ToString('N').ToUpper() | |
| } | |
| } | |
| # Create an instance of the WindowsInstaller.Installer object | |
| $installer = New-Object -ComObject WindowsInstaller.Installer | |
| # Get the list of installed programs and find the VMware Tools entry | |
| $vmwareTools = Get-CimInstance -Query "SELECT * FROM Win32_Product WHERE Name = 'VMware Tools'" -ErrorAction SilentlyContinue | |
| if ($vmwareTools) { | |
| # Extract the identifying number (GUID) | |
| [guid]$guid = $vmwareTools.IdentifyingNumber | |
| # Convert the guid to a packed guid | |
| [string]$packedguid = ConvertTo-PackedGuid $guid | |
| # Get the LocalPackage path from the registry | |
| $localPackage = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\${packedguid}\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 | |
| # Uninstall VMware Tools | |
| if ($Uninstall) { | |
| # Check if Force parameter is used or get user confirmation | |
| if ($Force) { | |
| $user_confirmed = "y" | |
| Write-Host "Force parameter specified - proceeding with uninstallation..." -ForegroundColor Yellow | |
| } | |
| else { | |
| $user_confirmed = Read-Host "Proceed with uninstallation? (y/n)" | |
| } | |
| if ($user_confirmed -eq "y") { | |
| Write-Host "Uninstalling VMware Tools..." -ForegroundColor Yellow | |
| Start-Process msiexec.exe -ArgumentList "/x `"${localPackage}`" /qn /norestart" -Wait | |
| Write-Host "Uninstallation completed." -ForegroundColor Green | |
| } | |
| else { | |
| Write-Host "Uninstallation cancelled by user." -ForegroundColor Yellow | |
| } | |
| } | |
| else { | |
| Write-Host "Dry run mode. To actually uninstall VMware Tools, run the script with the -Uninstall switch." -ForegroundColor Cyan | |
| return | |
| } | |
| } | |
| else { | |
| Write-Host "LocalPackage path not found in the registry." -ForegroundColor Red | |
| } | |
| } | |
| else { | |
| Write-Output "VMware Tools is not installed." | |
| } | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment