Search code examples
powershellpowershell-3.0

Variable is not sent to remote session


I have the code below that I have been working on and I have an issue, I can not get it to send the variable message to the computer, if I take out the variable it works but that is not what I was trying to accomplish with it.

Function Send-PopupMessage {
    #Requires -Version 2.0 
    [CmdletBinding()]  
    Param(
        [Parameter(Mandatory = $true)]
        [String]$ComputerName,
        [Parameter(Mandatory = $true)]
        [String]$Message
    )

    Invoke-Command -ComputerName $ComputerName -Scriptblock {
        $CmdMessage = "msg.exe * $Message"
        Write-Host $CmdMessage
        $CmdMessage | Invoke-Expression
    }
}

This is not the same as the question linked because I am in a session to another computer using PSWA so I am not able to start another session from this. Also even when I changed my code to be more like the one in the "Duplicate" question I am still getting the same results that the cmd being sent to the other computer is

msg.exe * '' instead of msg.exe * 'Test Message'


Solution

  • PowerShell script blocks are not lexical closures by default. The scriptblock passed to Invoke-Command does not save the current value of the $Message parameter when being run on the other computer.

    When the block is run in the remote session, it is using the current value of $Message in that session. Because that variable is most likely $null, the message is omitted from your command.

    Use the $using:variable syntax described in this question to capture the value of $Message.

    Invoke-Command -ComputerName $ComputerName -Scriptblock {
        $CmdMessage = "msg.exe * $using:Message"
        Write-Host $CmdMessage
        $CmdMessage | Invoke-Expression
    }
    

    The $using:variable syntax only works when invoking a block on a remote computer. If you need to capture variables in a scriptblock for local execution, instead call GetNewClosure() on the ScriptBlock.

    $Message = "Hey there."
    $closure = {
        $CmdMessage = "msg.exe * $Message"
        Write-Host $CmdMessage
        $CmdMessage | Invoke-Expression
    }.GetNewClosure()
    
    $Message = $null
    Invoke-Command -Scriptblock $closure