Vikrant Waje
Vikrant Waje

Reputation: 59

Passing scriptblock as an argument to Invoke-Command scriptblock in powershell

I have a powershell script in which I have been trying to pass scriptblock(i.e $sb) as an argument to another scriptblock. However, I keep getting error that:

Cannot convert the "{get-member}" value of type "System.String" to type "System.Management.Automation.ScriptBlock

a.psm1:

$sb = {get-member}
# $type = $sb.GetType().FullName => Returns scriptblock as data type
$result = Invoke-Command -Session "DC" -Scriptblock
{
    Param(
         [Parameter(Mandatory=$true)]
         [scriptblock]
         $sb
     )
 //Do some task
} -ArgumentList $sb

I am not able to figure out why $sb is treated as a string instead of Scriptblock?

The only way it works is changing the argument inside the Invoke-Command scriptblock to be of type string instead of Scriptblock. I am not sure why scriptblocks gets implicitly converted to string while passing argument to Invoke-Command scriptblock.

Upvotes: 3

Views: 1208

Answers (1)

mklement0
mklement0

Reputation: 437288

When a script block (type [scriptblock], { ... } as a literal) is passed to code that executes out-of-process, such as during remoting (your case) or in background jobs, XML-based serialization and deserialization must be performed.

On deserialization in the target process, [scriptblock] instances indeed unexpectedly become strings.

Unfortunately and bewilderingly, this behavior has been declared by design(!) - see GitHub issue #11698.

Your only option is to pass the script block('s source code) as a string, and convert it back to a script block via [scriptblock]::Create(); a simple example, using a background job:

Start-Job { 

  param([string] $scriptBlockSourceCode) # Note the type: [string]

  # Use [scriptblock]::Create() to turn the string into a script block,
  # and execute it with &
  & ([scriptblock]::Create($scriptBlockSourceCode))

} -ArgumentList { 'hi' } |
  Receive-Job -Wait -AutoRemove

Upvotes: 3

Related Questions