Spot checking Software Installs / EXEs

Hello All,

I have two conflicting reports and I’m trying to spot check computers for installed software to verify which report is more accurate.

I’ve tried variations of this but I’m not getting the output I’d like.

$ComputerNames = @(
    "PCSHP46"          #0
    "PCSHP45"          #1
    "VKSHPAIO10"       #2
    "VKSHPAIO19"       #3
)
$ComputerNames[0..3] | ForEach {
    Invoke-Command -ComputerName $($_) {Get-CimInstance Win32_Product | ? {$_.Name -like "*Office*"} | FT -AutoSize}
}

This does output correctly for each computer, however it does not list the computer name. So then I tried this.

$ComputerNames = @(
    "PCSHP46"          #0
    "PCSHP45"          #1
    "VKSHPAIO10"       #2
    "VKSHPAIO19"       #3
)
$ComputerNames[0..3] | ForEach {
    $Results = Invoke-Command -ComputerName $($_) {Get-CimInstance Win32_Product | ? {$_.Name -like "*Office*"} | FT -AutoSize}
}
$Results | Select-Object PSComputerName, Name, Caption, Vendor, Version, IdentifyingNumber

This output is not correct and only lists the last computer name, but not all of the other data.
PSComputerName : VKSHPAIO19
Name :
Caption :
Vendor :
Version :
IdentifyingNumber :

Also, for another application, it does not list an entry when I use Get-CimInstance so I was going to go with Test-Path to the EXE, but I will have to check both Program Files and Program Files (x86) as the install could reside in either.

    "PCSHP46"          #0
    "PCSHP45"          #1
    "VKSHPAIO10"       #2
    "VKSHPAIO19"       #3
)
$ComputerNames[0..3] | ForEach {
    Invoke-Command -ComputerName $($_) {Test-Path -Path "C:\Program Files (x86)\TeamViewer\TeamViewer.exe"}
    Invoke-Command -ComputerName $($_) {Test-Path -Path "C:\Program Files\TeamViewer\TeamViewer.exe"}
}

But this just outputs:
True
False
True
False

Hopefully someone can help me with these or if there is a better way in general, I would appreciate it.

You are overcomplicating and slowing it down. When you loop the computernames like this you run one at a time. Invoke-Command can run them all at once if you just give the list. The other issue is you are setting the variable inside the loop. I’ll show you how to collect output from a loop first, then we’ll fix the command to run efficiently.

For any loop where you need to collect data, just put the variable assignment before the loop.

$results = $computername | foreach-object {
    ... code that outputs objects
}
$ComputerNames = @(
    "PCSHP46"          #0
    "PCSHP45"          #1
    "VKSHPAIO10"       #2
    "VKSHPAIO19"       #3
)

$results = Invoke-Command -ComputerName $ComputerNames -Scriptblock {
    Get-CimInstance Win32_Product | Where-Object Name -like "*Office*"
}

It has also been noted that querying the Win32_Product class has poor performance and quite severe side effects.

Last note, Format-* cmdlets are for screen output only. They will break your lovely powershell objects.

2 Likes

Thank you krzydoug for all the info and help! I did not know that about Win32_Product. I mostly use it on test machines to check what software was installed and get the IdentifyingNumber, but it’s good to know for sure. What other options are there for that type of information though?

Also, now that this is working for Win32_Product, I adjusted it for Test-Path but I feel it’s not working properly. Also, I’m not sure how to display the result as well as PSComputerName.

$ComputerNames = @(
    "PCSHP46"
    "PCSHP45"
    "PCSHP44"
    "PCSHP42"
    "PCSHP41"
    "PCSHP39"
    "PCSHP38-L"
    "PCSHP29-L"
    "PCSHP26-L"
    "PCSHP14-L"
    "PCSHP14"
    "PCSHP13-L"
    "PCSHP10-L"
    "PCSHPAIO10"
    "PCSHPAIO19"
)

$Resultsx64 = Invoke-Command -ComputerName $ComputerNames -Scriptblock {
    Test-Path -Path "C:\Program Files\TeamViewer\TeamViewer.exe"
}

$Resultsx86 = Invoke-Command -ComputerName $ComputerNames -Scriptblock {
    Test-Path -Path "C:\Program Files (x86)\TeamViewer\TeamViewer.exe"
}

So, first off, the reason I say I think it’s not working properly, is that I get some WinRM connection errors, most likely because they are off or offline, however the results count is as if it ran against all computers in the array.

PS C:\Windows\System32> $Resultsx64
False
False
False
False
False
False
False
False
False
False
False
False
False

PS C:\Windows\System32> $Resultsx86
True
True
True
True
True
True
True
True
True
True
True
True
True

Second, how do I find out what property the result is? I’ve tried

$Resultsx86 | Get-Member
$Resultsx64 | Select-Object *
$Resultsx86 | Select-Object *

$Resultsx86 | Select-Object PSComputerName

It does store the PSComputerName, I’m just not sure how to show both PSComputerName and the result of True or False.

Thanks again!

# Store all paths in array
$product = @('C:\Program Files\TeamViewer\TeamViewer.exe',
'C:\Program Files (x86)\TeamViewer\TeamViewer.exe')

# Run test loop on remote systems then output results
 $result = Invoke-Command -ComputerName $ComputerNames -ScriptBlock {
foreach($p in $Using:product){[PSCustomObject]@{FullName=$p;Test=Test-Path $p}}
}

$result | Select-Object PSComputerName,FullName,Test
1 Like

Thank you so much random-commandline!

That worked perfectly!