[windows] How can I uninstall an application using PowerShell?

Is there a simple way to hook into the standard 'Add or Remove Programs' functionality using PowerShell to uninstall an existing application? Or to check if the application is installed?

This question is related to windows powershell windows-installer uninstallation

The answer is


Use:

function remove-HSsoftware{
[cmdletbinding()]
param(
[parameter(Mandatory=$true,
ValuefromPipeline = $true,
HelpMessage="IdentifyingNumber can be retrieved with `"get-wmiobject -class win32_product`"")]
[ValidatePattern('{[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}}')]
[string[]]$ids,
[parameter(Mandatory=$false,
            ValuefromPipeline=$true,
            ValueFromPipelineByPropertyName=$true,
            HelpMessage="Computer name or IP adress to query via WMI")]
[Alias('hostname,CN,computername')]
[string[]]$computers
)
begin {}
process{
    if($computers -eq $null){
    $computers = Get-ADComputer -Filter * | Select dnshostname |%{$_.dnshostname}
    }
    foreach($computer in $computers){
        foreach($id in $ids){
            write-host "Trying to uninstall sofware with ID ", "$id", "from computer ", "$computer"
            $app = Get-WmiObject -class Win32_Product -Computername "$computer" -Filter "IdentifyingNumber = '$id'"
            $app | Remove-WmiObject

        }
    }
}
end{}}
 remove-hssoftware -ids "{8C299CF3-E529-414E-AKD8-68C23BA4CBE8}","{5A9C53A5-FF48-497D-AB86-1F6418B569B9}","{62092246-CFA2-4452-BEDB-62AC4BCE6C26}"

It's not fully tested, but it ran under PowerShell 4.

I've run the PS1 file as it is seen here. Letting it retrieve all the Systems from the AD and trying to uninstall multiple applications on all systems.

I've used the IdentifyingNumber to search for the Software cause of David Stetlers input.

Not tested:

  1. Not adding ids to the call of the function in the script, instead starting the script with parameter IDs
  2. Calling the script with more then 1 computer name not automatically retrieved from the function
  3. Retrieving data from the pipe
  4. Using IP addresses to connect to the system

What it does not:

  1. It doesn't give any information if the software actually was found on any given system.
  2. It does not give any information about failure or success of the deinstallation.

I wasn't able to use uninstall(). Trying that I got an error telling me that calling a method for an expression that has a value of NULL is not possible. Instead I used Remove-WmiObject, which seems to accomplish the same.

CAUTION: Without a computer name given it removes the software from ALL systems in the Active Directory.


I will make my own little contribution. I needed to remove a list of packages from the same computer. This is the script I came up with.

$packages = @("package1", "package2", "package3")
foreach($package in $packages){
  $app = Get-WmiObject -Class Win32_Product | Where-Object {
    $_.Name -match "$package"
  }
  $app.Uninstall()
}

I hope this proves to be useful.

Note that I owe David Stetler the credit for this script since it is based on his.


I found out that Win32_Product class is not recommended because it triggers repairs and is not query optimized. Source

I found this post from Sitaram Pamarthi with a script to uninstall if you know the app guid. He also supplies another script to search for apps really fast here.

Use like this: .\uninstall.ps1 -GUID {C9E7751E-88ED-36CF-B610-71A1D262E906}

[cmdletbinding()]            

param (            

 [parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
 [string]$ComputerName = $env:computername,
 [parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,Mandatory=$true)]
 [string]$AppGUID
)            

 try {
  $returnval = ([WMICLASS]"\\$computerName\ROOT\CIMV2:win32_process").Create("msiexec `/x$AppGUID `/norestart `/qn")
 } catch {
  write-error "Failed to trigger the uninstallation. Review the error message"
  $_
  exit
 }
 switch ($($returnval.returnvalue)){
  0 { "Uninstallation command triggered successfully" }
  2 { "You don't have sufficient permissions to trigger the command on $Computer" }
  3 { "You don't have sufficient permissions to trigger the command on $Computer" }
  8 { "An unknown error has occurred" }
  9 { "Path Not Found" }
  9 { "Invalid Parameter"}
 }

For Most of my programs the scripts in this Post did the job. But I had to face a legacy program that I couldn't remove using msiexec.exe or Win32_Product class. (from some reason I got exit 0 but the program was still there)

My solution was to use Win32_Process class:

with the help from nickdnk this command is to get the uninstall exe file path:

64bit:

[array]$unInstallPathReg= gci "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" | foreach { gp $_.PSPath } | ? { $_ -match $programName } | select UninstallString

32bit:

 [array]$unInstallPathReg= gci "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall" | foreach { gp $_.PSPath } | ? { $_ -match $programName } | select UninstallString

you will have to clean the the result string:

$uninstallPath = $unInstallPathReg[0].UninstallString
$uninstallPath = $uninstallPath -Replace "msiexec.exe","" -Replace "/I","" -Replace "/X",""
$uninstallPath = $uninstallPath .Trim()

now when you have the relevant program uninstall exe file path you can use this command:

$uninstallResult = (Get-WMIObject -List -Verbose | Where-Object {$_.Name -eq "Win32_Process"}).InvokeMethod("Create","$unInstallPath")

$uninstallResult - will have the exit code. 0 is success

the above commands can also run remotely - I did it using invoke command but I believe that adding the argument -computername can work


Based on Jeff Hillman's answer:

Here's a function you can just add to your profile.ps1 or define in current PowerShell session:

# Uninstall a Windows program
function uninstall($programName)
{
    $app = Get-WmiObject -Class Win32_Product -Filter ("Name = '" + $programName + "'")
    if($app -ne $null)
    {
        $app.Uninstall()
    }
    else {
        echo ("Could not find program '" + $programName + "'")
    }
}

Let's say you wanted to uninstall Notepad++. Just type this into PowerShell:

> uninstall("notepad++")

Just be aware that Get-WmiObject can take some time, so be patient!


Here is the PowerShell script using msiexec:

echo "Getting product code"
$ProductCode = Get-WmiObject win32_product -Filter "Name='Name of my Software in Add Remove Program Window'" | Select-Object -Expand IdentifyingNumber
echo "removing Product"
# Out-Null argument is just for keeping the power shell command window waiting for msiexec command to finish else it moves to execute the next echo command
& msiexec /x $ProductCode | Out-Null
echo "uninstallation finished"

One line of code:

get-package *notepad* |% { & $_.Meta.Attributes["UninstallString"]}

To add a little to this post, I needed to be able to remove software from multiple Servers. I used Jeff's answer to lead me to this:

First I got a list of servers, I used an AD query, but you can provide the array of computer names however you want:

$computers = @("computer1", "computer2", "computer3")

Then I looped through them, adding the -computer parameter to the gwmi query:

foreach($server in $computers){
    $app = Get-WmiObject -Class Win32_Product -computer $server | Where-Object {
        $_.IdentifyingNumber -match "5A5F312145AE-0252130-432C34-9D89-1"
    }
    $app.Uninstall()
}

I used the IdentifyingNumber property to match against instead of name, just to be sure I was uninstalling the correct application.


To fix up the second method in Jeff Hillman's post, you could either do a:

$app = Get-WmiObject 
            -Query "SELECT * FROM Win32_Product WHERE Name = 'Software Name'"

Or

$app = Get-WmiObject -Class Win32_Product `
                     -Filter "Name = 'Software Name'"

function Uninstall-App {
    Write-Output "Uninstalling $($args[0])"
    foreach($obj in Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall") {
        $dname = $obj.GetValue("DisplayName")
        if ($dname -contains $args[0]) {
            $uninstString = $obj.GetValue("UninstallString")
            foreach ($line in $uninstString) {
                $found = $line -match '(\{.+\}).*'
                If ($found) {
                    $appid = $matches[1]
                    Write-Output $appid
                    start-process "msiexec.exe" -arg "/X $appid /qb" -Wait
                }
            }
        }
    }
}

Call it this way:

Uninstall-App "Autodesk Revit DB Link 2019"

EDIT: Over the years this answer has gotten quite a few upvotes. I would like to add some comments. I have not used PowerShell since, but I remember observing some issues:

  1. If there are more matches than 1 for the below script, it does not work and you must append the PowerShell filter that limits results to 1. I believe it's -First 1 but I'm not sure. Feel free to edit.
  2. If the application is not installed by MSI it does not work. The reason it was written as below is because it modifies the MSI to uninstall without intervention, which is not always the default case when using the native uninstall string.

Using the WMI object takes forever. This is very fast if you just know the name of the program you want to uninstall.

$uninstall32 = gci "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall" | foreach { gp $_.PSPath } | ? { $_ -match "SOFTWARE NAME" } | select UninstallString
$uninstall64 = gci "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" | foreach { gp $_.PSPath } | ? { $_ -match "SOFTWARE NAME" } | select UninstallString

if ($uninstall64) {
$uninstall64 = $uninstall64.UninstallString -Replace "msiexec.exe","" -Replace "/I","" -Replace "/X",""
$uninstall64 = $uninstall64.Trim()
Write "Uninstalling..."
start-process "msiexec.exe" -arg "/X $uninstall64 /qb" -Wait}
if ($uninstall32) {
$uninstall32 = $uninstall32.UninstallString -Replace "msiexec.exe","" -Replace "/I","" -Replace "/X",""
$uninstall32 = $uninstall32.Trim()
Write "Uninstalling..."
start-process "msiexec.exe" -arg "/X $uninstall32 /qb" -Wait}

To fix up the second method in Jeff Hillman's post, you could either do a:

$app = Get-WmiObject 
            -Query "SELECT * FROM Win32_Product WHERE Name = 'Software Name'"

Or

$app = Get-WmiObject -Class Win32_Product `
                     -Filter "Name = 'Software Name'"

Examples related to windows

"Permission Denied" trying to run Python on Windows 10 A fatal error occurred while creating a TLS client credential. The internal error state is 10013 How to install OpenJDK 11 on Windows? I can't install pyaudio on Windows? How to solve "error: Microsoft Visual C++ 14.0 is required."? git clone: Authentication failed for <URL> How to avoid the "Windows Defender SmartScreen prevented an unrecognized app from starting warning" XCOPY: Overwrite all without prompt in BATCH Laravel 5 show ErrorException file_put_contents failed to open stream: No such file or directory how to open Jupyter notebook in chrome on windows Tensorflow import error: No module named 'tensorflow'

Examples related to powershell

Why powershell does not run Angular commands? How do I install the Nuget provider for PowerShell on a unconnected machine so I can install a nuget package from the PS command line? How to print environment variables to the console in PowerShell? Check if a string is not NULL or EMPTY The term 'ng' is not recognized as the name of a cmdlet VSCode Change Default Terminal 'Connect-MsolService' is not recognized as the name of a cmdlet Powershell Invoke-WebRequest Fails with SSL/TLS Secure Channel Install-Module : The term 'Install-Module' is not recognized as the name of a cmdlet Change directory in PowerShell

Examples related to windows-installer

How to avoid the "Windows Defender SmartScreen prevented an unrecognized app from starting warning" How can I find the product GUID of an installed MSI setup? Batch script to install MSI How to uninstall with msiexec using product id guid without .msi file present Create an application setup in visual studio 2013 Error 1920 service failed to start. Verify that you have sufficient privileges to start system services How to install .MSI using PowerShell Create MSI or setup project with Visual Studio 2012 AppFabric installation failed because installer MSI returned with error code : 1603 How to create an installer for a .net Windows Service using Visual Studio

Examples related to uninstallation

How to uninstall Eclipse? How to remove docker completely from ubuntu 14.04 How to completely uninstall kubernetes How to uninstall Anaconda completely from macOS How to Completely Uninstall Xcode and Clear All Settings Remove composer How can I find the product GUID of an installed MSI setup? How to uninstall mini conda? python Force uninstall of Visual Studio How to remove a package from Laravel using composer?