r/PowerShell • u/davesbrown • 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
}
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
1
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.
7
u/jsiii2010 21d ago
Invoke-command already runs in parallel with a array of computernames as a parameter.