Search code examples
powershellloopsjobsstart-jobforeach-object

Collecting data with jobs inside a ForEach-Object loop


The goal is to have an object that shows the computer's ($poste) collections information. I'm trying to collect that data with jobs so it runs much faster. However, I'm struggling getting it right. I'm trying:

$poste = "xx"
$SiteCode = "XX"
$SiteServer = "XX"

$ResourceID = Get-WmiObject -Namespace root\sms\site_$SiteCode -Computer $SiteServer -Class SMS_R_SYSTEM -Filter "Name='$poste'" |
              Select-Object -ExpandProperty ResourceID

$NomsCollection = Get-WmiObject -Namespace root\sms\site_$SiteCode -ComputerName $SiteServer -Class sms_fullcollectionmembership -Filter "ResourceID ='$resourceID'" |
                  ForEach-Object {
                      Invoke-Command -ScriptBlock {
                          Get-WmiObject -Namespace root\sms\site_$SiteCode -Computer $SiteServer -Class SMS_COllection -Filter "CollectionID='$($_.CollectionID)'"
                      } -Computer localhost -AsJob
                      Get-Job | Receive-Job
                  }

$NomsCollection |
    Select-Object CollectionID, Name, Comment, ObjectPath |
    Sort-Object -Property Name |
    Out-GridView -Title $poste -Wait

All I end up with in the gridview are the names of the jobs (job1, job3, etc.).

If I do it "normally", without jobs, it works fine but takes a while to complete, hence my idea of running the commands in the ForEach-Object as jobs.

For example if I do this I get the results I want:

$poste = "xx"
$SiteCode = "xx"
$SiteServer = "xx"

$ResourceID = Get-WmiObject -Namespace root\sms\site_$SiteCode -computer $SiteServer -Class SMS_R_SYSTEM -Filter "Name='$poste'" |
              Select-Object -ExpandProperty ResourceID

$NomsCollection = Get-WmiObject -Namespace root\sms\site_$SiteCode -ComputerName $SiteServer -Class sms_fullcollectionmembership -Filter "ResourceID ='$resourceID'" |
                  ForEach-Object {
                      Get-WmiObject -Namespace root\sms\site_$SiteCode -Computer $SiteServer -Class SMS_COllection -Filter "CollectionID='$($_.CollectionID)'"
                  }

$NomsCollection |
    Select-Object CollectionID, Name, Comment, ObjectPath |
    Sort-Object -Property Name |
    Out-GridView -Title $poste -Wait

What am I missing in the way to approach the jobs within the ForEach-Object?

EDIT: Thanks to the answer below, here is the final code I used, which differs slightly:

$SiteCode = "xx"
$SiteServer = "xx"

$ResourceID = Get-WmiObject -Namespace root\sms\site_$SiteCode -computer $SiteServer -Class SMS_R_SYSTEM -Filter "Name='$poste'" |
Select-Object -ExpandProperty ResourceID

$jobs = Get-WmiObject -Namespace root\sms\site_$SiteCode -ComputerName $SiteServer -Class sms_fullcollectionmembership -Filter "ResourceID ='$resourceID'" |
ForEach-Object {
    Start-Job -ArgumentList $SiteServer, $SiteCode, $_.CollectionID -ScriptBlock {
        Param ($server,$code,$id)
        Get-WmiObject -Namespace root\sms\site_$code -Computer $server -Class SMS_COllection -Filter "CollectionID='${id}'"
    } 
}

$NomsCollection = $jobs | Get-Job | Wait-Job | Receive-Job


$NomsCollection | Select-Object CollectionID, Name, Comment, ObjectPath | Sort-Object -Property Name | Out-GridView -Title $poste -Wait

Solution

  • You need to pass the variables that were defined outside the job into the job:

    Invoke-Command -ScriptBlock {
        Param($server, $code, $id)
        Get-WmiObject -Namespace root\sms\site_$code -Computer $server -Class SMS_COllection -Filter "CollectionID='${id}'"
    } -Computer localhost -ArgumentList $SiteServer, $SiteCode, $_.CollectionID -AsJob
    

    Also, you need to wait for the jobs to finish before collecting the output:

    $jobs = Get-WmiObject -Namespace ... | ForEach-Object {
        Invoke-Command ...
    }
    $NomsCollection = $jobs | Get-Job | Wait-Job | Receive-Job