Search code examples
powershellpowershell-remoting

can not pass a variable filled in a script block to a function outside of the block in powershell


trying to pass a date from a remote computer to a function in my PS Script. but just gives me junk. from reading i saw that you can not use variables that were declared and intiated inside of a scriptblock on the outside. not sure how to get the date from these PCs. tried to use "$using" and also "$script" but that does not work. get-date works fine directly called to screen

    #Fucntion to manipulate the data
Function writeToServer
{
  param($server,$Time)

  # Data preparation for loading data into SQL table 
  $InsertResults = @"
  INSERT INTO [ServerTimeSync].[dbo].[ServerTimes](SystemName,ShownTimeOnServer)
  VALUES ('$SERVER','$Time')
  "@      
  #call the invoke-sqlcmdlet to execute the query
     Invoke-sqlcmd @params -Query $InsertResults
 }



  foreach ($COMPUTER in $COMPUTERS){ 
  icm $COMPUTER -ScriptBlock {$ENV:COMPUTERNAME

    $computer 
    get-date -Format "MM-dd-yyyy hh:mm:ss tt"

    $script:sdate = get-date -Format "MM-dd-yyyy hh:mm:ss tt"                        
    } 
      writeToServer $computer $script:sdate      
}

Solution

    • You can only pass (the values of) local variables TO a remote command, via the $using: scope.

      • $using: references are embedded directly in the remote script block and are an alternative to passing values via arguments (parameters), using -ArgumentList - see this answer for examples. In both cases only the value of a local variable is getting passed, not the variable object itself.
    • The only way to report values FROM a remote command is to have it produce output, which you can capture in a variable on the caller's side ($output = Invoke-Command ...) or process in a pipeline, as shown below.

      • That is, you fundamentally cannot set variables for the caller from a remote script block.

    Note that the above applies to all out-of-runspace / cross-process calls, e.g. also to Start-Job - see this answer for details.

    You can also streamline your command by using parallel processing:

    Invoke-Command -ComputerName $COMPUTERS -ScriptBlock {
      Get-Date -Format "MM-dd-yyyy hh:mm:ss tt"
    } | 
      ForEach-Object {
        writeToServer $_.PSComputerName $_      
      }
    

    Note:

    • If you pass multiple computer names to Invoke-Command's -ComputerName parameter, processing occurs in parallel, but the ordering of the outputs isn't guaranteed to match the order in which the names were passed.

    • A ForEach-Object call is used to process each object received from the remote call, which inside the script block you can refer to via the automatic $_ variable.

    • PowerShell automatically decorates all output objects from a remote script block with ETS (Extended Type System) properties that reflect the invocation context; notably, .PSComputerName contains the name of the remote computer.