powershell

How to wrap an existing scriptblock in a new scriptblock


I am writing a function to execute commands on a remote machine. The function receives a scriptblock and needs to wrap those commands inside additional commands (i.e. a try / catch). However, I need to expand local variables before remote execution. As an example, I created the below script:

function Child {
  param(
    [scriptblock]$command
  )

  write-host "Command: $command"
  # Command: echo $using:var

  $session = New-PSSession

  invoke-command -session $session -command $command
  # My Variable

  invoke-command -session $session -command {
    try {
        $using:command
    } catch {}
  }
  # echo $using:var

  Remove-PSSession $session
}

Function Parent {
  $var = "My Variable"
  
  Child -command {
    echo $using:var
  } 
}

Parent

I can see that the Child function receives the command without the variable expanded. This is expected because $using only works inside certain expressions i.e. invoke-command. Next, the first invoke-command executes the $command directly. You can see that the variable is expanded before being sent to the remote machine. Finally, the second invoke-command I try to wrap $command in a try / catch block. In this case, the local variable is not expanded as it is sent directly to the remote machine, as is.

How can I wrap, or expand on, the instructions of an existing scriptblock like this?

In addition, I would much prefer to expand the local variables in the Parent function before Child receives it. Is that possible?


Solution

  • To solve this issue I create a new scriptblock and embed the original scriptblock as a string in the new scriptblock which allows the new scriptblock to expand the $using variables included in the original scriptblock. Because of this, variables that are "local" to the "remote host" are escaped in the new scriptblock. I imagine that same thing could be done in the parent function to expand the variables before it is passed to the child function.

    function Child {
      param(
        [scriptblock]$command
      )
    
      write-host "Command: $command"
      # Command: echo $using:var
    
      $session = New-PSSession
    
      invoke-command -session $session -command $command
      # My Variable
    
      $newCommand = [scriptblock]::Create("
        `$errorActionPreference = 'stop'
        try {
            $command
        } catch {
            throw `$_ | Select-Object ScriptStackTrace, Exception, ErrorDetails
        }
      ")
    
      invoke-command -session $session -command $newCommand
      # My Variable
    
      Remove-PSSession $session
    }
    
    Function Parent {
      $var = "My Variable"
      
      Child -command {
        echo $using:var
      } 
    }
    
    Parent