7

I know, that with the Parameter $using:foo I can use a Variable from a different runspace while running ForEach-Object -Parallel in Powershell 7 and up.

But how can I add the result back to a Variable? The common parameters += and $using: will not work.

For instance:

$AllSubs = Get-AzSubscription
$Date = (Get-Date).AddDays(-2).ToString("yyyy-MM-dd")
$Costs = @()

$AllSubs | Sort-Object -Property Name | ForEach-Object -Parallel {
    Set-AzContext $_.Name | Out-Null
    Write-Output "Fetching Costs from '$($_.Name)' ..."
    $using:Costs += Get-AzConsumptionUsageDetail -StartDate $using:Date -EndDate $using:Date -IncludeAdditionalProperties -IncludeMeterDetails -ErrorAction SilentlyContinue
}

Output:

The assignment expression is not valid. The input to an assignment operator must be an object that is
     | able to accept assignments, such as a variable or a property.
Gill-Bates
  • 559
  • 8
  • 22

1 Answers1

7

You will have to split the operation into two and assign the reference you get from $using:Costs to a local variable, and you will have to use a different data type than PowerShell's resizable array - preferably a concurrent (or thread-safe) type:

$AllSubs = Get-AzSubscription
$Date = (Get-Date).AddDays(-2).ToString("yyyy-MM-dd")

# Create thread-safe collection to receive output
$Costs = [System.Collections.Concurrent.ConcurrentBag[psobject]]::new()

$AllSubs | Sort-Object -Property Name | ForEach-Object -Parallel {
    Set-AzContext $_.Name | Out-Null
    Write-Output "Fetching Costs from '$($_.Name)' ..."

    # Obtain reference to the bag with `using` modifier 
    $localCostsVariable = $using:Costs

    # Add to bag
    $localCostsVariable.Add($(Get-AzConsumptionUsageDetail -StartDate $using:Date -EndDate $using:Date -IncludeAdditionalProperties -IncludeMeterDetails -ErrorAction SilentlyContinue))
}
Mathias R. Jessen
  • 157,619
  • 12
  • 148
  • 206
  • I'm wondering, does this type of collection work on jobs / runspace? – Santiago Squarzon Jun 17 '21 at 12:42
  • 2
    The wiring will be different (you'd use a SessionStateProxy variable to provide the reference to the collection), but sure – Mathias R. Jessen Jun 17 '21 at 12:47
  • Nicely done. An alternative to using a local variable is to enclose the `$using:` reference in `(...)`: `($using:Costs).Add(...)`. Arguably, neither should be necessary - see [GitHub issue #10876](https://github.com/PowerShell/PowerShell/issues/10876) – mklement0 Jun 17 '21 at 13:25