I want to pass a ScriptBlock as an argument into a Start-Job ScriptBlock. I understand that [ScriptBlock]
is serialized to a string for safety when passed into a job. So, I need to deserialize it. I've tried using [ScriptBlock]::Create()
but it doesn't seem to process the $($args[0].cheese)
correctly.
This is a simplied version of what I'm doing.
function Format-Text {
[CmdletBinding()]
param([Parameter(Mandatory, ValueFromPipeline)][Object]$InputObject,
[Parameter(Mandatory)][ScriptBlock]$Formatter)
Write-Host ($Formatter.Invoke($InputObject))
}
$formatter = {"My favourite cheese is: $($args[0].cheese)"}
$testObject = [PSCustomObject]@{cheese = 'cheddar'}
Format-Text -InputObject $testObject -Formatter $formatter
# stuff before this is just to demonstrate that it works outside of Start-Job
$job = Start-Job -ArgumentList $testObject,$formatter.ToString() -ScriptBlock {
param ([String]$Obj,
[String]$Fmtr)
function Format-Text {
[CmdletBinding()]
param([Parameter(Mandatory, ValueFromPipeline)][Object]$InputObject,
[Parameter(Mandatory)][ScriptBlock]$Formatter)
Write-Host ($Formatter.Invoke($InputObject))
}
$sb = [ScriptBlock]::Create($Fmtr)
Format-Text -InputObject $Obj -Formatter $sb
}
# clean up
do { Start-Sleep -Milliseconds 500 } until ($job.State -ne 'Running')
Receive-Job $job; Remove-Job $job
The output is:
My favourite cheese is: cheddar
My favourite cheese is:
How can I deserialize the string
such that the $($args[0].cheese)
works?
The example above is pared down to the bone, the real script is 00s of lines and many functions. I don't want to rewrite the function if I can avoid it because it's used in many other locations.
I'm running the built-in PowerShell 5.1.
Recreating your script block from a string using [scriptblock]::Create()
works as intended.
As an aside: the alleged security considerations that resulted in choosing to deserialize script blocks as strings have never been spelled out. Personally, I don't see any risk associated with deserializing script blocks as such, given that construction of a script block does not result in its execution.
See GitHub issue #11698 for a discussion.
Your only problem is the accidental typing of your -Object
parameter as [String]$Obj
, whereas it should be [object] $Obj
(or simply $Obj
).
A [string]
object obviously doesn't have a .cheese
property, and, by default, PowerShell quietly evaluates attempts to access non-existent properties as $null
, which in the context of string expansion (interpolation) evaluates to the empty string (which is what you saw).
You could have caught this problem via Set-StrictMode
-Version 2
or higher, which reports attempts to access non-existent properties as errors, though note the additional constraints imposed by these modes.