Uninstall Sofware using registry key

I am trying to build something to uninstall APC Powerchute Network Shutdown. I need to do a few things here:

1 - Go through AD and find the machines (Done)
(Get-ADComputer -SearchBase “OU=Servers,DC=domain=ca” -filter *).name

2 - Get the registry value (Done)
Get-RegString -Key ‘SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\powerchute network Shutdown’ -Value UninstallString

3 - Now I need to uninstall using the uninstall key but the problem is that I can’t figure out how to pass all this through

Any ideas?

Here is what I have:

Get-QADComputer -Searchroot “OU=Servers,DC=DOMAIN,DC=ca”| ForEach-Object {
[PSCustomObject] @{
Servername = $.name
online = Test-Connection $
.name -Count 1 -Quiet
}
} | Export-Csv -Path c:\Results.csv -NoTypeInformation
# selecting only computers online | where {$_.name online}

| Foreach-Object{
Get-RegString -Key ‘SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\powerchute network Shutdown’ -Value UninstallString
}

Hey Marc,

If I understand right, you’re wanting to do remote uninstalls?

In case you might come across some systems that do not have the software installed, or have for whatever reason a slightly different uninstall cmd, it’s probably best to do the reading of the registry key within the remote scriptblock. Based on whether or not you get a result from trying to read this key, you can then carry out the rest of the script for uninstalling.

Here’s a rough guide to the type of thing I think you’d need to do. The cmd /c $uninstallCMD should allow you to execute the command without the need for parsing the arguments. Bear in mind that when you run this, the remote session will be frozen until the uninstallation ends. I’ve not checked the code entirely, so you may need to do a couple of changes.

Get-QADComputer -Searchroot 'OU=Servers,DC=DOMAIN,DC=ca'|
ForEach-Object -Process {
    [PSCustomObject] @{
        Servername = $_.name
        online     = Test-Connection -ComputerName $_.name -Count 1 -Quiet
    }
} |
Export-Csv -Path c:\Results.csv -NoTypeInformation


$Results = Import-Csv -Path c:\Results.csv | Where-Object -FilterScript {
    $_.name -eq 'online'
}

#Let's start doing the remote uninstalls

ForEach ($computername in $Results.servername) 
{
    Invoke-Command -ComputerName $computername -ScriptBlock {
        $ErrorAction = 'Stop'
        Try 
        {
            $UninstallCMD = Get-RegString -Key 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\powerchute network Shutdown' -Value UninstallString
        }
        Catch 
        {

        }
        If ($UninstallCMD) 
        {
            cmd.exe /c $UninstallCMD
        }
    }
}

Thanks Tim, you are correct I am trying to setup a remote uninstall for software that does not show up in Add/Remove programs so I need to get the uninstall string from the registry and use that string to uninstall the software.

I have worked with your suggestions and it seems to work fine for single machines. When I try to pipe in the Active Directory machines from the Get-QADComputer command I see to be losing the computer name in the test-connection part. Below is the error. Can you see why I would be losing that? Thanks in advance for your help.

Test-Connection : Cannot validate argument on parameter 'ComputerName'. The argument is null or empty. Supply an argument that is not null or empty and then try the command again. At line:10 char:52 + online = Test-Connection -ComputerName $_.name -Count 1 -Quiet + ~~~~~~~ + CategoryInfo : InvalidData: (:) [Test-Connection], ParameterBindingValidationException + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.TestConnectionCommand

Actually Tim, I must have had a typo. The Test-Connection does work. I will test the remainder and let you know how it goes. Thanks!

I have a functioning script now that seems to work as expected. Thanks for your valuable input!!

I have added a few pieces and removed the PSremoteRegistry commands because I was having trouble with it.

I don’t have anything output or logging after the $uninstallcmd. So one more thing that would be nice to add would be another output file that will possibly give me the info from $uninstallcmd

I tried a pipe but was getting nothing. Maybe you can help with that?

add-pssnapin quest.activeroles.admanagement $Application = "Powerchute network shutdown"

Get-QADComputer -Searchroot ‘OU=Servers,DC=domain=ca’|
ForEach-Object -Process {
[PSCustomObject] @{
Servername = $.name
online = Test-Connection -ComputerName $
.name -Count 1 -Quiet
}
} |
Export-Csv -Path c:\Results.csv -NoTypeInformation

$Results = Import-Csv -Path c:\Results.csv | Where-Object -FilterScript {
$_.name -eq ‘online’
}

#Let’s start doing the remote uninstalls

ForEach ($computername in $Results.servername)
{
Invoke-Command -ComputerName $computername -ScriptBlock {
$ErrorAction = ‘Stop’
Try
{

    $UninstallCMD = Get-ChildItem HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall | 

Get-ItemProperty | Where-Object {$_.DisplayName -eq $Application } | Select -ExpandProperty UninstallString
}
Catch
{

    }
    If ($UninstallCMD) {
    
        cmd.exe /c $UninstallCMD -q
    }
}

}

Hi Tim,

It seems like when I run the entire script against the target machines from the CSV file, nothing happens. I don’t have any errors but the program doesn’t get uninstalled. Can we insert some logging to see what’s happening?

Thanks
Marc

Hi Marc,

I think it’ll be because you refer to $application within your scriptblock, when it’s defined outside of it.

A scriptblock does not have visibility of variables outside of it unless you pass them in as arguments, or alternatively refer to them via $using:variablename.

Try changing the line to

Get-ItemProperty | Where-Object {$_.DisplayName -eq [b]$Using:Application[/b] } | Select -ExpandProperty UninstallString

Hi Tim, the $USING is new to me. I have tried it remotely and get nothing. I tried running it locally and get the following error since it can’t be run against local machines.

PS C:\windows> Get-ChildItem -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall |
>> Get-ItemProperty |
>> Where-Object {$_.DisplayName -eq $Using:Application} |
>> Select-Object -Property UninstallString
>>
A Using variable cannot be retrieved. A Using variable can be used only with
Invoke-Command, Start-Job, or InlineScript in the script workflow. When it is
used with Invoke-Command, the Using variable is valid only if the script block
is invoked on a remote computer.
At line:3 char:23

  •     Where-Object {$_.DisplayName -eq $Using:Application} |
    
  •                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    • CategoryInfo : InvalidOperation: (:slight_smile: [], RuntimeException
    • FullyQualifiedErrorId : UsingWithoutInvokeCommand

I think the error is within your Import-CSV filterscript. You’re filtering based on ‘name’ equals ‘online’. But the CSV only contains the property ‘servername’ (not name) and the property online with a boolean True or False.

This would fix that:

$Results = Import-Csv -Path C:\Results.csv | Where-Object -FilterScript {
$_.Online -eq $True
}

Thanks, I will test the Import-CSV filterscript as you mentioned. I have been testing and I see another potential issue. When I run the individual pieces, I see that the Uninstall string is “…\Program files\APC\Powerchute\uninstall.exe”

The $uninstallCMD properly holds the value but there is a space in “…\Program Files” so the uninstall command fails. I think I need to store the Uninstall Command where spaces will be tolerated.

I can’t figure out how to get that value and store it with spaces. I have tried various usage of “quotation marks”, ‘back ticks’.

The term ‘C:\Program’ is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct
+ CategoryInfo : ObjectNotFound: (C:\Program:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
+ PSComputerName : MARCW2K12

Any ideas on this would be great!

Thanks
Marc

I have a function that handles my uninstalls - they’re ‘locally’ called rather than remotely, however for non msiexec.exe uninstall commands, I have the following code blocks. I don’t know if this will help in your instance, but just in case…

# Remove double quotes from string
$UninstallCmd = $UninstallCmd -replace '"'

# String up to and including the .exe
$UninstallExecutible = $UninstallCmd.substring(0,$UninstallCmd.IndexOf(".exe") +4 )

# Any parts after the .exe		
$UninstallArguments = $UninstallCmd.substring($UninstallCmd.IndexOf(".exe") +4 )

Then the uninstall is handled with:

$RetVal = Start-Process -FilePath "$UninstallExecutible" -ArgumentList "$UninstallArguments" -Wait -Passthru

Hello Guys,

My version of it… if it helps…

#Checking for Installation.
 $check = (Get-ItemProperty -path 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{xxxxx-xx-x-x-x-x--xxx-x-x}}_is1' -ErrorAction SilentlyContinue | select-object -ExpandProperty "DisplayName" | findstr "xxxxxx")

  if( $check -like '*xxx*')
 {
    Write-Output "$check available"
    Stop-Service -Name XXXXXXX # if any service dependednt.
    $checks_uninstall = (Get-ItemProperty -path 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{xxxx-x-x-x-x-x-xxxx-x-x-x-x-x-x}}_is1' | select-object -ExpandProperty "QuietUninstallString")
    if($checks_uninstall -eq '')
    {
        Write-Output "Uninstallation Failed!"
    }
    else
    {
        $checks_uninstall = $checks_uninstall -replace '"',''
        $scriptBlock = [Scriptblock]::Create($checks_uninstall)
        Invoke-Command -ScriptBlock $scriptBlock
        Write-Output "Uninstallation Completed! "
    }

 }

hope it helps!

Regards,
V.

Thank you very much for all this great feedback! As this was getting too complicated and taking too much time to figure out with Powershell I decided to go with a .bat file to uninstall and applied it through a group policy at start up. Really simple and gets the job done. If anyone is interested the code is below.

REM Perform clean uninstall of Powerchute Netwok Shutdown
 echo Perform clean uninstall of Powerchute Netwok Shutdown
 if exist "C:\Program Files\APC\PowerChute\uninstall.exe" "C:\Program Files\APC\PowerChute\uninstall.exe" -q
 if exist "D:\Program Files\APC\PowerChute\uninstall.exe" "D:\Program Files\APC\PowerChute\uninstall.exe" -q
 if exist "E:\Program Files\APC\PowerChute\uninstall.exe" "E:\Program Files\APC\PowerChute\uninstall.exe" -q
 echo Removal Successful