Uninstalling using Get-WmiObject

2019-03-02 10:23发布

问题:

I'm trying to run a PowerShell command in a batch script. Im trying to remove all traces of an old RMM tool from client PCs and for the life of me can't get this line to run correctly.

The idea is that this searches for software that has N-Able in the vendor name and passes the GUID to the $nableguid variable and then msiexec.exe against the GUIDs found above.

PowerShell -NoProfile -ExecutionPolicy Bypass -Command "& {$nableguid = (Get -WmiObject Win32_Product -Filter "vendor LIKE '%N-able%'" | Select -ExpandProperty IdentifyingNumber); foreach ($nguid in $nableguid){ & MsiExec.exe /X$nguid /quiet} }";

The current error is as follows

Get-WmiObject : A positional parameter cannot be found that accepts argument ''. 
At line:1 char:18
+ & {$nableguid = (Get-WmiObject Win32_Product -Filter vendor LIKE '' | Select -Ex ...
+                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [Get-WmiObject], ParameterBindingException
    + FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.GetWmiObjectCommand

I know that this boils down to orders of operations and the current organization of quotations, but after messing around with the ordering for an hour I still cant get it to work correctly.

回答1:

Check this snippet, it is using WMI but in another way, and has almost never failed me :

function Uninstall-Application($computer, $target) {
    $productInfo = Get-WmiObject Win32_Product -Filter "Name LIKE '%$target%'" -ComputerName $computer
    $pin = $productInfo.IdentifyingNumber
    $pn = $productInfo.Name
    $pv = $productInfo.Version

    if($pn -ne $null) {
        $classKey = "IdentifyingNumber=`"$pin`",Name=`"$pn`",version=`"$pv`""

        $uninstallReturn = ([wmi]"\\$computer\root\cimv2:Win32_Product.$classKey").uninstall()
        if($uninstallReturn.ReturnValue -ge 0) { Write-Host "Uninstall complete" }
        else { $uninstallReturn | Out-Host }
    } else {
        Throw "Product not found"
    }
}

Example usage :

Uninstall-Application "127.0.0.1" "firefox"


回答2:

You're getting that error, because you're using unescaped percent characters and double quotes inside a double-quoted string:

"& {... -Filter "vendor LIKE '%N-able%'" ...}"

The above evaluates to -Filter vendor LIKE '' when the PowerShell command is executed, i.e. only the token vendor is passed as an argument to the parameter -Filter. LIKE and '' are passed as separate positional parameters.

You must escape the nested double quotes to preserve them for the PowerShell command:

"& {... -Filter \"vendor LIKE '%N-able%'\" ...}"

You must also double the % characters (that's how they are escaped in batch/CMD), otherwise %N-able% would be interpreted as a batch variable and expanded to an empty string before the execution of the powershell commandline.

"& {... -Filter \"vendor LIKE '%%N-able%%'\" ...}"

With that said, in general it would be far simpler to implement the PowerShell code in a PowerShell script:

$nableguid = Get -WmiObject Win32_Product -Filter "vendor LIKE '%N-able%'" |
             Select -ExpandProperty IdentifyingNumber)

foreach ($nguid in $nableguid) {
  & MsiExec.exe /X$nguid /quiet
}

and run that script via the -File parameter (if you must run it from a batch file in the first place):

powershell.exe -NoProfile -ExecutionPolicy Bypass -File "C:\path\to\script.ps1"

An even better approach would be to drop the batch script entirely and implement everything in PowerShell.