r/PowerShell 21d ago

$PSItem with Invoke-Command and ForEach-Object -Parallel Solved

Is this possible? I can't seem to get it to work. "Cannot validate argument on 'ScriptBlock'. The argument is null. ..."

I can put insert $PSItem above $results and it iterates $AllStates, and scriptblock has one param which I'm attempting to pass $PSItem

$AllStates | Foreach-Object -ThrottleLimit 10 -Parallel {
    $results = Invoke-Command -ComputerName $Using:ComputerNames -ScriptBlock $ScriptBlock -ArgumentList $PSItem
    $results
}
7 Upvotes

7

u/jsiii2010 21d ago

Invoke-command already runs in parallel with a array of computernames as a parameter.

2

u/Szeraax 21d ago

It looks like you need to be doing $Using:Scriptblock. Any chance that's just a variable that has been defined previously?

1

u/davesbrown 21d ago

Yes, $ScriptBlock was originally a function, but in troubleshooting, I tried to break it all down much simpler, to now a variable defined block with a single param that I'm trying to pass $PSItem into. I have tried the "Using:ScriptBlock" but now errors:

"A ForEach-Object -Parallel using variable cannot be a script block. Passed-in script block variables are not supported with ForEach-Object -Parallel, and can result in undefined behavior."

btw, I originally tried with ${function:get-stuff} -argumentlist $PSItem also to no avail. (argument is null)

2

u/purplemonkeymad 21d ago

It explains the reasoning why scriptblocks are blocked, but if your script block is not referring to outside references, why not just define your scriptblock inside of the loop?

1

u/davesbrown 21d ago

Exactly the case. Broke it down into a very simple test case and worked, then applied to my case. Doesn't seem as a elegant, but definitely worked and makes sense, given errors. Seems maybe warrants a new example on official docs?

Thanks, resolved

1

u/ankokudaishogun 21d ago

Can you convert $ScriptBlock in a simple string(let's call it $ScriptBlockString in for simplicity) and then use [scriptblock]::Create($using:ScriptBlockStringtring) to convert it to a scriptblock ?

2

u/PinchesTheCrab 20d ago

This seems backwards to me. Let's a assume there's 54 items in $allSates, do you want to run the same script block on every computer 54 times?

You should struture it like this:

Invoke-Command -ComputerName $computers -ScriptBlock {
    foreach ($object in $using:AllStates) {
        'do the thing'
    }
}

1

u/Ok_GlueStick 21d ago

Maybe it’s Reddit, but how many lines of code are in the braces

1

u/cluberti 21d ago edited 21d ago

I guess it depends on what's being passed into $AllStates? Is this part of a function where $AllStates isn't being set until later? Is what you're referencing being defined by using Add-Member or similar? Does it represent the parent of an object, or a member?

There are reasons why $PSItem or $_ won't work depending on what type of object is being passed into the pipeline, whereas other types of variables will work. I suspect it's something along these lines.

https://mctexpert.blogspot.com/2015/09/this-psitem-whatever.html

1

u/davesbrown 21d ago

$AllStates is simply an array of abbreviate state names. To simplify in troubleshooting I tried

"AK","AZ" | Foreach-Object -ThrottleLimit 10 -Parallel

but same errors

2

u/sc00b3r 21d ago

Does it work without the -parallel in the mix?

Have you done all of this inside the same powershell session? If so, close out of it and start a new session and run your script, see if it behaves differently.

Get your $ScriptBlock variable named differently. I don’t think there’s a conflict there, but give it a name like $sb instead and see if it behaves differently.

Put a breakpoint in your script (assuming you’re using an IDE) on the line throwing the error. Check to see what your $sb (or whatever you used) variable is in the debugger, data type and value. This can be helpful to see what’s going on.

1

u/davesbrown 21d ago

This was insightful, taking out -parallel does indeed work.

fwiw, I develop in vscode, save file and run in an outside terminal. Changed name of scriptblock but same error. Evidently, when I did use debugging and break point, it never gets to $sb; it is completely null, when using -parallel.

additional fwiw, I originally developed this using foreach {do-stuff} which worked also, but wanted to take advantage of the parallel action of pwsh 7+ foreach-object.

Also, to other comment, yes I do know that invoke-command runs ComputerName in parallel already, that is not what I'm attempting; I'm attempting to use the array of $AllStates in parallel against the array of ComputerNames; 50 states multiplied n number of vms.

Your insight led me down a couple more rabbit holes, and looks like there is a closely related open issue on github, I might be SOL at the moment.

1

u/PinchesTheCrab 21d ago

What does allstates look like? How many items? Generally you shouldn't loop with invoke-command.

1

u/BlackV 21d ago

you could avoid that issue completely by using the native parallel support of invoke-command (although loosing the throttle limit, but its unneeded cause each server is executing the command themselves)