There is a script that has CmdletBinding
attribute, which effectively makes it an “advanced” script. Inside the script I'm processing data in a pipeline in parallel, and I want the -WhatIf
parameter to be passed down to the processing script block when I pass it to the script invocation.
Simplified code:
#Requires -Version 7.2
[CmdletBinding(SupportsShouldProcess = $true)]
param()
Get-ChildItem | ForEach-Object -Parallel {
if ($PSCmdlet.ShouldProcess("target", "operation")) {
Write-Host "Processing"
}
}
PS C:\> script.ps1 -WhatIf
InvalidOperation:
Line |
2 | if ($PSCmdlet.ShouldProcess("target", "operation")) {
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| You cannot call a method on a null-valued expression.
This does not work because $PSCmdlet
is not defined in the script block.
When I replace $PSCmdlet
with ($using:PSCmdlet)
, I get another error (only when -WhatIf
is provided):
MethodInvocationException:
Line |
2 | if (($using:PSCmdlet).ShouldProcess("target", "operation")) {
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| Exception calling "ShouldProcess" with "2" argument(s): "The WriteObject and WriteError methods cannot be called from outside the overrides of the BeginProcessing, ProcessRecord, and EndProcessing methods, and they can only be called from within the same thread. Validate that the cmdlet makes these calls correctly,
or contact Microsoft Customer Support Services."
Obviously, this happens because script blocks are executed in seaprate threads (“they can only be called from within the same thread”).
How to properly handle -WhatIf
inside the script blocks of Foreach-Object -Parallel
?
I've read this official article and seen this comment to the PowerShell issue #13816. Maybe another related issue: #14984.
As a side-note: specifying -WhatIf
to the ForEach-Object
itself doesn't make any difference in this case. This is also noticed here: https://thedavecarroll.com/powershell/foreach-object-whatif/#script-blocks-and--whatif