Zachary Isom
Zachary Isom

Reputation: 63

Powershell 7 ForEach-Object Parallel breaks automatic variable when passed to invoke-command

Please advise, any insight as to what i need to do to have the variable passed successfully would be much appreciated.

This works successfully, but works on each FQDN in the piped list one at a time. i have 100+ servers, so this can take longer than one would think. like 1-6 seconds per server


    Write-Host "Confirm correct OS is installed" -ForegroundColor Yellow -BackgroundColor Black

    $FQDNs | ForEach-Object {
        Invoke-Command -ComputerName $_ -Credential $Credentials -ScriptBlock { 
            $OS = (Get-CimInstance -ClassName CIM_OperatingSystem).Caption
            Write-Host "$Using:_`: $OS" -ForegroundColor Green -BackgroundColor Black
            Write-Output "$Using:_`: $OS"
        }
    }
} 

if i add the -Parallel parameter, it fails immediately with the error below. How else am i supposed to give the variable if an automatic variable is the only way I'm seeing that foreach-object pipes them? (I'm hoping that's wrong)

ForEach-Object: C:\Scripts\Checklist.ps1:53
Line |
  53 |      $FQDNs | ForEach-Object -Parallel {
     |               ~~~~~~~~~~~~~~~~~~~~~~~~~~
     | The value of the using variable '$using:_' cannot be retrieved because
     | it has not been set in the local session.

Here's the script with the Parallel parameter inserted to show exactly where i'm doing that


    Write-Host "Confirm correct OS is installed" -ForegroundColor Yellow -BackgroundColor Black

    $FQDNs | ForEach-Object -Parallel {
        Invoke-Command -ComputerName $_ -Credential $Credentials -ScriptBlock { 
            $OS = (Get-CimInstance -ClassName CIM_OperatingSystem).Caption
            Write-Host "$Using:_`: $OS" -ForegroundColor Green -BackgroundColor Black
            Write-Output "$Using:_`: $OS"
        }
    }
}

Upvotes: 1

Views: 2007

Answers (2)

coryseaman
coryseaman

Reputation: 387

The problem is apparently that PowerShell doesn't always handle nested contexts properly, and in this case, because adding the -Parallel parameter creates an independent runspace for each script block instance running in parallel, the $Using: now evaluates for that runspace context rather than the remoting session context in which it was previously executed without the parameter.

You'll actually run into the same problem if you alternately use Invoke-Command with -ThrottleLimit and an array of computer names passed into -ComputerName to take advantage of parallelism rather than ForEach-Object with -Parallel.

To address this, try using [scriptblock]::Create() to build your script block so that the correct context is honored, as described in this answer.

Upvotes: 2

js2010
js2010

Reputation: 27423

Invoke-command computername comp1,comp2,comp3 already runs in parallel.

# elevated prompt
start-service winrm
invoke-command localhost,localhost,localhost { sleep 10 } 
(get-history)[-1] | fl

Id                 : 3
CommandLine        :     invoke-command localhost,localhost,localhost { sleep 10 }
ExecutionStatus    : Completed
StartExecutionTime : 6/19/2020 10:05:02 AM
EndExecutionTime   : 6/19/2020 10:05:13 AM  # 11 seconds for all three

Upvotes: 1

Related Questions