Run a ForEach loop asynchronously?

This topic contains 4 replies, has 4 voices, and was last updated by  Aurock 4 years ago.

  • Author
  • #9485


    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 $ {
            write-output $i.Name
            Get-WmiObject  -ComputerName $ -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!

  • #9486


    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.

  • #9487

    Dave Wyatt

    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 ( 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.

  • #9500


    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

  • #9543


    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.

You must be logged in to reply to this topic.