Author Posts

August 14, 2013 at 11:50 am

I have a small script I put together to search for workstations with a specific application involved. The script gets a list of computers running Windows 7 from AD, checks whether each is online, then does a wmi query for Name in Win32_Product on that computer. It works, but it's slow. Between the time required for the wmi query and the fact that it runs them one at a time, it takes hours to complete.

Is it possible to make it run the wmi query against multiple computers at once, rather than one at a time?

`$Computers = (get-adcomputer -server chc-dcrd -filter {operatingsystem -like "Windows 7*"} -Property Name,OperatingSystem | select -first 5)

foreach($i in $Computers){
    if(test-connection -quiet -count 1 -cn $i.name) {
        write-output $i.Name
        Get-WmiObject  -ComputerName $i.name -Query "Select * from Win32_Product Where Name like '%$Key%'" |
        select -property PSComputerName,Version |
        export-csv -path $CSVFile -append
    }
    else {out-file [string]$i.Name -filepath c:\temp\Offline.txt -append}
}

`

This is my first post here, I recently discovered the site thanks to the incredible Getting Started with Powershell 3.0 jump start a few weeks ago.

Thanks in advance for any suggestions you can offer!

August 14, 2013 at 2:03 pm

Yes, part of the problem is that a foreach loop is serial. So it will take a very long time to run.

Another problem that you're faced with is that Win32_Product is not query optimized. Microsoft recommends that you do not query it. So please keep that in mind. For example try running your get-wmiobject query on just your computer and see how long it takes. I ran Get-WmiObject -Class win32_product -Filter "name=software123" and it took 2 minutes to run.

However, using your code here is what I came up with:

invoke-command -computername (get-adcomputer -server chc-dcrd -filter {operatingsystem -like "Windows 7*"}).name -scriptblock {Get-WmiObject -Query "Select * from Win32_Product Where Name like '%$Key%'"}

I would also suggest that you consider making this into a function with parameters. You can could then pass in computer names from the get-adcomputer.

Finally, I would recommend writing your script to query the uninstall registry key. I think you might find it much faster.

August 14, 2013 at 5:07 pm

Get-WmiObject has an -AsJob parameter which would probably be helpful here. Unlike the Start-Job cmdlet, workflows, etc (which all start up multiple PowerShell sessions), the WMI -AsJob option is multithreaded (runs in a single PowerShell session).

I have a script up on the TechNet Script Center repository (http://gallery.technet.microsoft.com/scriptcenter/Multithreaded-PowerShell-0bc3f59b) that is an example of how you can use this approach. It's based around the Test-Connection cmdlet, but the same rules apply for Get-WmiObject.

August 15, 2013 at 6:56 am

You could also look at workflows if you are using PowerShell 3.0 or later. Workflows can leverage script blocks with commands that are executed in parallel, and they also support a -Parallel switch on the foreach statement, allowing for parallel execution of a script block while referencing a parameter (the item in a command being processed). For more information see:

Import-Module PSWorkflow
help about_workflows
help about_Foreach-Parallel

August 15, 2013 at 2:51 pm

Thanks for the replies. I'm playing with a couple of the suggestions, but I think Lery is probably on the right track, and I'll end up trying to query the registry key instead of wmi.