Check If a program is installed on multiple computers using Powershell

Leon Sterling picture Leon Sterling · Mar 30, 2015 · Viewed 15.1k times · Source

I'm having issue with a script I've written and would love some help. Please note I'm very new to powershell.

I've written a script that uses a txt file that contains remote computers on a domain, I appears to be working to some degree but in the event of a machine being offline I get errors which then loop the script.

$machines
$pcname
Name = 'Machine'
Expression = { $_.PsComputerName }
}

ForEach ($System in $Machines)
{
    #Pings machine's found in text file
    if (!(test-Connection -ComputerName $System -BufferSize 16 -Count 1 -ea 0 -Quiet))
    {
        Write-Output "$System Offline"
    }

Else
   {
     #Providing the machine is reachable 
     #Checks installed programs for products that contain Kaspersky in the name
     gwmi win32_product -Filter {Name like "%Kaspersky%"} -ComputerName $Machines | Select-Object -Property $pcname,Name,Version
   }
}

At present this runs and output's like so:

Machine          Name                                        Version
UKTEST01         Kaspersky Security Center Network Agent     10.1.249 
UKTEST02         Kaspersky Endpoint Security 10 for Windows  10.2.1.23

But in the event of a machine not being reachable the following error is given:

gwmi : The RPC server is unavailable. (Exception from HRESULT: 0x800706BA)
At C:\Scripts\Powershell\Kaspersky Endpoint Security 10\Script\New folder\Kaspersky Checker working v2.ps1:15 char:9
+         gwmi win32_product -Filter {Name like "%Kaspersky%"} -ComputerName   $Mach ...
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : InvalidOperation: (:) [Get-WmiObject], COMException
+ FullyQualifiedErrorId : GetWMICOMException,Microsoft.PowerShell.Commands.GetWmiObjectCommand

And then moves to the next machine in the list, and then repeats from the beginning again.

I'd like for this to simply show as:

UKTEST03 Offline

And stop once the last machine in the txt file is done.

Any help or advise would be greatly appreciated.

Answer

FoxDeploy picture FoxDeploy · Mar 30, 2015

This is the perfect time to use a Try/Catch/Finally block. The flow is this : Try the block of code here, if you encounter an error, suppress the message and do what is in the Catch block instead.

I've modified your code a bit, so simply copy this whole code block and drop it in, replacing your Else {scriptblock} in your original code.

Else
{
 #Providing the machine is reachable 
 #Checks installed programs for products that contain Kaspersky in the name
 Try {Get-WMIObject -Class win32_product -Filter {Name like "%Kaspersky%"} `
   -ComputerName $Machines -ErrorAction STOP | 
      Select-Object -Property $pcname,Name,Version }
 Catch {#If an error, do this instead
        Write-Output "$system Offline }
  }
}

Your completed answer

I've folded in the change you requested, to keep your script from running on every machine in $machines instead of $system, as you likely intended.

ForEach ($System in $Machines){
    #Pings machine's found in text file
    if (!(test-Connection -ComputerName $System -BufferSize 16 -Count 1 -ea 0 -Quiet))
    {
        Write-Output "$System Offline"
    }
    Else
    {
     #Providing the machine is reachable 
     #Checks installed programs for products that contain Kaspersky in the name
     Try {Get-WMIObject -Class win32_product -Filter {Name like "%Kaspersky%"} `
       -ComputerName $System -ErrorAction STOP | 
          Select-Object -Property $pcname,Name,Version }
     Catch {#If an error, do this instead
            Write-Output "$system Offline "}
     #EndofElse
     }
#EndofForEach
}