Reputation: 185
I have this custom uninstaller script that I made. The idea behind it is it's universal and is made to work with uninstalling msiexec as well as executable uninstallers (pulling the uninstall strings from the registry). Now the caveat to this is; I'm noticing several msi installations have uninstall strings that show /I as a switch instead of /X; which doesn't make sense to me and I assume that's incorrect.
In my script I check to see if the parameter has /I and I replace it with /X. Similarly, if the uninstall doesn't have a silent uninstall string, I am trying to append it into my uninstaller so it doesn't require any user interaction. When I try to append it, I get the msiexec window that comes up with all the switches; which tells me that the way I'm passing the switches is incorrect.
Here is an example when I try to uninstall Teams. I print out the variables for the uninstaller and the arguments passed.
Output looks like this (which tells me it should work):
MsiExec.exe
/X{731F6BAA-A986-45A4-8936-7C3AAAAA760B} /qn /norestart
I'm hoping someone can assist me. Here is my script.
CLS
$Software = "Teams"
$Filter = "*" + $Software + "*"
$Program = $ProgUninstall = $NULL
try
{
if (Test-Path -Path "HKLM:\SOFTWARE\WOW6432Node")
{
$programs = Get-ItemProperty -Path "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" -ErrorAction Stop
}
$programs += Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*" -ErrorAction Stop
$programs += Get-ItemProperty -Path "Registry::\HKEY_USERS\*\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*" -ErrorAction SilentlyContinue
}
catch
{
Write-Error $_
break
}
foreach($Program in $Programs)
{
$ProgDisplayName = $Program.DisplayName
$ProgUninstall = $Program.UninstallString
if(($ProgDisplayName -like $Filter) -and ($NULL -ne $ProgUninstall))
{
$aux = $ProgUninstall -split @('\.exe'),2,[System.StringSplitOptions]::None
$Uninstaller = (cmd /c echo $($aux[0].TrimStart('"').TrimStart("'") + '.exe')).Trim('"')
$UninsParams = $aux[1].TrimStart('"').TrimStart("'").Trim().split(' ',[System.StringSplitOptions]::RemoveEmptyEntries)
if($UninsParams -like "/I*")
{
$UninsParams = $UninsParams -replace("/I", "/X")
}
if($Uninstaller -like "MsiExec.exe")
{
$UninsParams = "$UninsParams /qn /norestart"
}
#Debug
$Uninstaller
$UninsParams
try
{
. $Uninstaller $UninsParams | Where-Object { $_ -notlike "param 0 = *" }
}
Catch
{
write-host "Unable to uninstall: " $Uninstaller $UninsParams
}
}
}
Upvotes: 0
Views: 298
Reputation: 36297
The problem that you have is that you create a string array at this line:
$aux = $ProgUninstall -split @('\.exe'),2,[System.StringSplitOptions]::None
So $aux is an array of strings, but you try to treat it like a single string later on. If you want it to act like a string not an array you'll need to join it at some point. Either that or don't use cmd to execute the uninstall and keep it as an array (that's what I'd do). If you want to keep it an array you could use Start-Process
instead, and then add your arguments to the correct parameter. Something like:
foreach($Program in $Programs)
{
$ProgDisplayName = $Program.DisplayName
$ProgUninstall = $Program.UninstallString
if(($ProgDisplayName -like $Filter) -and ($NULL -ne $ProgUninstall))
{
$aux = $ProgUninstall -split '(?<=\.exe[''"]?) ',2,[System.StringSplitOptions]::None
$Uninstaller = $aux[0].trim('''"')
$UninsParams = $aux[1].Trim() -split ' (?=/|-)',[System.StringSplitOptions]::RemoveEmptyEntries
if($Uninstaller -eq "MsiExec.exe")
{
[string[]]$UninsParams = $UninsParams -replace "/I", "/X"
[string[]]$UninsParams = $UninsParams|?{$_ -notin '/qn','/norestart'}
[string[]]$UninsParams += '/qn'
[string[]]$UninsParams += '/norestart'
}
#Debug
$Uninstaller
$UninsParams
Try
{
Start-Process -FilePath $Uninstaller -ArgumentList $UninsParams
}
Catch
{
write-host "Unable to uninstall: $Uninstaller $UninsParams"
}
}
}
That'll split your parameters on a space followed by /
or -
, which should follow most things that take parameters. If you just want to do it on the spaces you could change the line to:
$UninsParams = $aux[1].Trim() -split ' ',[System.StringSplitOptions]::RemoveEmptyEntries
Upvotes: 1