Home > Back-end >  How do I change which Powershell version is used in a remote session?
How do I change which Powershell version is used in a remote session?

Time:08-27

I have a Powershell script which creates a session to a virtual machine. In this remote session I want the machine to read data from a json file and return a custom object from that data, hence I am using the -AsHashtable flag for the ConvertFrom-Json command to do this. However this flag was introduced in Powershell 6 and when I write the powershell version from the remote session to the output the session displays "5.1" as powershell version...

The virtual machine has Powershell 7.2.5 installed and when manually logging onto the vm and launching Powershell 7 and executing the commands it operates as expected. How do I tell the session in my script to use Powershell 7?

CodePudding user response:

This code assumes that the PowerShell ZIP package has been downloaded and unzipped to the remote computer's C:\Users\Public\PSv7\ path. If PowerShell 7 is installed with an installer on the remote computer, then C:\Users\Public\PSv7\pwsh.exe can probably be reduced to pwsh. The -ExecutionPolicy RemoteSigned is probably not needed, but I would recommend keeping -NoProfile for performance.

$ComputerName = 'RemotePCName'
$Temp = Invoke-Command -ComputerName $ComputerName -ScriptBlock {
    if($PSVersionTable.PSVersion.Major -lt 7) {
        return (C:\Users\Public\PSv7\pwsh.exe -NoProfile -ExecutionPolicy RemoteSigned $MyInvocation.MyCommand.ScriptBlock)
    }
    else {
        return "[$($Env:ComputerName)] {$($PSVersionTable.PSVersion)}"
    }
}
Write-Host "$Temp"

The code:

  1. Runs a ScriptBlock on the remote system, saving result to $Temp.
  2. In ScriptBlock: If the current PowerShell version is less than 7, then call pwsh passing self's ScriptBlock.
  3. In ScriptBlock: In Else block, return the current machine's name and PowerShell version as a string.
  4. After exit of ScriptBlock, Write $Temp to Host, showing the machine name and PowerShell version where the else part of the script was ran.

Commands, such as ConvertFrom-Json -AsHashtable, should run fine inside the else statement.

This is the first time I tried passing a ScriptBlock to pwsh.exe from within PowerShell, so, it appears to work, but I'm not sure how pwsh.exe is receiving the ScriptBlock or returning info on exit. I wouldn't be surprised if only [string] values can be returned from pwsh.exe.

Basic Parameter Version:

This version flips the if statement so the else part is where pwsh is called, and accepts any arguments.

$ComputerName = 'RemotePCName'
$MyData1 = 'ArbitraryData1'
$MyData2 = 'ArbitraryData2'
$Temp = Invoke-Command -ComputerName $ComputerName -ArgumentList $MyData1, $MyData2 -ScriptBlock {
    if($PSVersionTable.PSVersion.Major -ge 7) {
        return "[$($Env:ComputerName)] {$($PSVersionTable.PSVersion)} ($($args[0]), $($args[1]))"
    }
    else {
        return (C:\Users\Public\PSv7\pwsh.exe -NoProfile -ExecutionPolicy RemoteSigned $MyInvocation.MyCommand.ScriptBlock -args $args)
    }
}
Write-Host "$Temp"

Defined Parameter Version:

This version is the similar to the above basic parameter version, except the parameters are explicitly defined.

$ComputerName = 'RemotePCName'
$MyData1 = 'ArbitraryData1'
$MyData2 = 'ArbitraryData2'
$Temp = Invoke-Command -ComputerName $ComputerName -ArgumentList $MyData1, $MyData2 -ScriptBlock {
    param (
        [Parameter(Mandatory = $true, Position = 0)]
        [string]$Param1,
        [Parameter(Mandatory = $true, Position = 1)]
        [string]$Param2
    )
    if($PSVersionTable.PSVersion.Major -ge 7) {
        return "[$($Env:ComputerName)] {$($PSVersionTable.PSVersion)} ($Param1, $Param2)"
    }
    else {
        return (C:\Users\Public\PSv7\pwsh.exe -NoProfile -ExecutionPolicy RemoteSigned $MyInvocation.MyCommand.ScriptBlock -args $Param1, $Param2)
    }
}
Write-Host "$Temp"

Update:

Interesting! At the time of creating the above examples I was largely throwing things at the wall and seeing what sticks. But, when looking into -Command, it I found the following interesting statements:

The Command parameter only accepts a script block for execution when it can recognize the value passed to Command as a ScriptBlock type. This is only possible when running pwsh from another PowerShell host.

and:

When called from within an existing PowerShell session, the results are returned to the parent shell as deserialized XML objects, not live objects.

Haven't done any experiments yet, but this gives me hope that complex objects can be returned from a call to pwsh.exe.

  • Related