Using uninstallstring through PowerShell to uninstall program - powershell

The issue I'm having right now is that when I go to run the code through PowerShell, it is changing the value of the uninstall string and adding the variable name before it. The result i'm hoping for is this.
MsiExec.exe /X{2C5B24AD-9F13-52A1-KA2N-8K4A41DC9L4G}
But the result I'm getting from the variable after replacing the /I with an /X and doing .Trim() is the following:
#{UninstallString=/X{2C5B24AD-9F13-52A1-KA2N-8K4A41DC9L4G}}
So I was wondering if you guys would be able to tell me from my code below where I'm going wrong.
I have to replace the /I with /X, because the uninstall string first comes back like this MsiExec.exe /I{2C5B24AD-9F13-52A1-KA2N-8K4A41DC9L4G}, and I'm trying to uninstall, not install.
if ($Uninstall_str) {
#run uninstall here
try {
$Uninstall_str = $Uninstall_str -replace 'MsiExec.exe /I', '/X'
$Uninstall_str = $Uninstall_str.Trim()
Start-Process "msiexec.exe" -Arg "$Uninstall_str /qb" -Wait
} catch {
Write-Output $_.Exception.Message
Write-Output $_.Exception.ItemName
Write-Warning "Error unintalling."
}
}

You didn't expand the UninstallString value when reading it from the registry. Your code for doing that probably looks somewhat like this:
$Uninstall_str = Get-ItemProperty 'HKLM:\...\Uninstall\Something' |
Select-Object UninstallString
Replace that with
$Uninstall_str = Get-ItemProperty 'HKLM:\...\Uninstall\Something' |
Select-Object -Expand UninstallString
and the problem will disappear.

To get rid of the #{uninstallstring all I needed to do was specify what I was trimming on this line
$Uninstall_str = $Uninstall_str.Trim()
So that line changed to the following to receive the results I was looking for.
$Uninstall_str = $Uninstall_str.Trim("#{UninstallString=")

Related

Get-WindowsUpdateLog stream re-direction

Has anyone noticed how the Get-WindowsUpdateLog cmdlet cannot be redirected to any streams?
Furthermore, storing the output into a variable, piping it, or any type of re-direction leads to the cmdlet to only be executed.
Any help redirecting/silencing the output of this command would be appreciated.
What I've tried:
Get-WindowsUpdateLog | Out-Null
Get-WindowsUpdateLog > $null
$sink = Get-WindowsUpdateLog
Everything I could find failed to suppress the output of the CmdLet Get-WindowsUpdateLog. As you say, the displayed information in the console is not properly following the output streams as we know them in PowerShell.
The only workaround I found is using Jobs:
$Job = Start-Job -ScriptBlock {Get-WindowsUpdateLog}
$Job | Wait-Job | Remove-Job
This way all output is handled within the job and we don't retrieve the result. It's also unnecessary to retrieve it as the result is simply a text file placed in the -Path parameter.
As an addendum to DarkLite's answer, we can also utilise the following code to check if Get-WindowsUpdateLog cmdlet worked properly :
# Generate a unique filename.
$o_filename = "C:\temp\" + "WindowsUpdateLog" + "_" + ( Get-Date -UFormat "%H_%M_%S" ) + ".txt"
$o_job = Start-Job -ScriptBlock { Get-WindowsUpdateLog -LogPath "$( $args[0] )" } `
-ArgumentList $o_filename -ErrorAction SilentlyContinue
Start-Sleep -Seconds 5
$o_job | Remove-Job -ErrorAction SilentlyContinue
if( ! ( Test-Path $o_filename ) ) {
# Return an exception
}
else {
# Do some work on the generated log file
}
I think the lesson here is don't use Out-Default in a script. https://keithga.wordpress.com/2018/04/03/out-default-considered-harmful/ I don't see that command in powershell 6. But it is in powershell 7!

uninstall installed programs using powershell using partial match

I have some programs installed in Windows 10 and 7 that start with (in Uninstall Programs)
"Python info.data-5.332234" "Python delta.ind-5.332234" "Python
module.data-15.332234" "Python hatch.back-0.332234"
I've tried various scripts to try and uninstall using partial match with PowerShell, but none of them seems to uninstall the programs.
This is the latest script I've used and does not work... it uninstalls the registry entry but not actually remove the folder or the entry from Uninstall Programs
$remove = #('Python info.data', 'Python delta.ind', 'Python module.data', 'Python hatch.back')
foreach ($prog_name in $remove) {
Write "Uninstalling" % $prog_name
$uninstall32 = gci "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall" | foreach { gp $_.PSPath } | ? { $_ -match $prog_name } | select UninstallString
if ($uninstall32) {
$uninstall32 = $uninstall32.UninstallString -Replace "msiexec.exe","" -Replace "/I","" -Replace "/X",""
$uninstall32 = $uninstall32.Trim()
Write "Uninstalling..."
Write $uninstall32
start-process "msiexec.exe" -arg "/X $uninstall32 /qn" -Wait}
}
The Problem is, that the variable $uninstall32 can contain more than 1 entry.
Add a $uninstall32.GetType() before you start msiexec to check if the variable may contains more than one string. If so, msiexec wont run because you are passing two GUIDs at once.
Use the Win32_Product WMI Class to get the GUIDs of the desired Applications.
$remove = #('Python info.data', 'Python delta.ind', 'Python module.data', 'Python hatch.back')
foreach ($prog_name in $remove) {
$uninstall32 = #(Get-WmiObject -Query "SELECT * FROM Win32_Product WHERE Name like '$prog_name%'")
foreach ($product in $uninstall32) {
Write "Uninstalling...`n$($product.Name)"
$exitCode = (Start-Process "msiexec.exe" -ArgumentList "/X $($product.IdentifyingNumber) /qn" -Wait -PassThru).ExitCode
Write "$($product.Name) return ExitCode: $exitCode"
}
}
Also, add the -PassThrue Switch at the Start-Process CMDLet and catch/output the ExitCode of each Uninstallation Process.
Make sure your Powershell/ISE is running with elevated privileges.

Powershell script exits on run time when Invoke-expression is executed

I am trying to execute below code, when the run time hits the first invoke-expression , the script exit with out running the remaining code .ps1 file . This is happening in windows 7 with powershell v3. Any pointers here is appreciated. I have tried using Try{Invoke-expression ""} catch{ $_ }, but the logs show that script exit.
$HardWares=#("abc","def")
Write-Info ("Deleting device driver with $application")
foreach ($HardWare in $HardWares){
Write-Info ("working on hardware $HardWare")
$DriverID = (Get-WmiObject -Class Win32_PnpSignedDriver | Where-Object {$_.hardwareID -eq $HardWare} | Select-Object InfName -ExpandProperty InfName | Select-Object -Unique)
if ($DriverID){
Write-Info ("Removing $HardWare and deleting $DriverID")
$HardwareRemoveCmd = "D:\Users\App.exe remove $HardWare"
Invoke-Expression $HardwareRemoveCmd
$Command= "D:\Users\App.exe dp_delete $DriverID -f"
Invoke-Expression $Command
} else {Write-Info "Could not find $DriverID file for $HardWare"}
}
The issue might be that you've created a variable called $DriverID, yet you're checking if $Driverid returns true. Variable creation is case sensitive so you have two different variables here. This will return false causing the script to skip over into the else statement.
If you want "did not find any $Driverid" to be written to the display, you'll need to put "Write-Host" in front of it.
A tidied up version of your code.
{
$Driverid = (Get-WmiObject -Class Win32_PnpSignedDriver | Where-Object
{$_.hardware -eq $HardWare} | Select-Object InfName -ExpandProperty InfName
| Select-Object -Unique)
if($Driverid)
{
Invoke-Expression "D:\Users\App.exe remove $HardWare"
$Command= "D:\Users\App.exe dp_delete $Driverid -f"
Invoke-Expression $Command
}
else
{
Write-Host "did not find any $Driverid"
}
}
Hope this helps!

want to see a message on powershell ISE while executing .exe

I am trying to execute this powershell script
Write-output `n "\\Latest Modified Mobile file is"
$dir = "\\sharepoint.amr.ith.intel.com#SSL\sites\peca\PTA Data Sandbox\Shared Documents\Under_review\Regina\Estimates"
$filter="*EstimateImport_Mobile_*.xlsx"
$latest = Get-ChildItem -Path $dir -Filter $filter | Sort-Object LastAccessTime -Descending | Select-Object -First 1
$latest.Name
if($?)
{
"command succeeded"
}
else
{
"command failed"
}
& "C:\PECA Data Estimates Importer\PECA Estimates Importer.exe" import -i $latest.FullName
if($?)
{
"command succeeded"
}
else
{
"command failed"
}
Start-Sleep -s 5
exit
when I am using the "&" operator, it runs some windows .exe tool but on the screen I don't see any message, I want to see a message like "Running tool" on a powershell ISE so that the user knows that the script is still working.
If you want to see output on the screen, don't use Write-Output.
Use Write-Host to write text that will always be seen. Use Write-Verbose to write text that will only be seen in verbose mode.

How do I set up autocomplete in powershell 2.0?

I have a drop down menu that's populated by a CSV file. I had to change my code around to make it work with v2. The last thing I can't figure out is autocomplete. I'd like to be able to type in the listbox and have it suggest some options from the list. It works fine in v3, but not v2.
Here are the parts that matter...
$filecheck = "$dir\Apps\Customers.csv"
If(Test-Path -path $filecheck) {
$Customers = Import-CSV "$dir\Apps\Customers.csv" -Header $headers
$List = $customers | Select-Object -Expand name | where {$_ -ne ""} | where {$_ -ne "Name"} | Sort-Object
}
ForEach ($Items in $List) {
$companybox.Items.Add($Items)
}
$companybox.AutoCompleteSource = 'CustomSource'
$companybox.AutoCompleteMode = 'SuggestAppend'
$List | % {$companybox.AutoCompleteCustomSource.AddRange($_) }
$Form1.Controls.Add($companybox)
Any help would be greatly appreciated. I've looked around but haven't found much for v2.
Thanks.
Got it to work. I added -STA to the batch file that runs the script to change it to Single Thread Apartment.
My batch file now looks like this to run the script in STA as administrator...
SET Dir=%~dp0
SET PSPath=%Dir%Script.ps1
PowerShell -NoProfile -ExecutionPolicy Bypass -Command "& {Start-Process PowerShell -ArgumentList '-STA -NoProfile -ExecutionPolicy Bypass -File ""%PSPath%""' -Verb RunAs}";
Another option is to put this in the PS script...
if ([System.Threading.Thread]::CurrentThread.ApartmentState -eq [System.Threading.ApartmentState]::MTA)
{
powershell.exe -Sta -File $MyInvocation.MyCommand.Path
return
}
However, the second option broke the code I have to hide the console window on startup.