How to start a new PowerShell session from another PS session, and run a CmdLet with splatted Parameters How to start a new PowerShell session from another PS session, and run a CmdLet with splatted Parameters powershell powershell

How to start a new PowerShell session from another PS session, and run a CmdLet with splatted Parameters


Note: In principle, the techniques in this answer can be applied not only to calling from Windows PowerShell to PowerShell Core, but also in the opposite direction, as well as to between instances of the same PowerShell edition, both on Windows and Unix.

You don't need Start-Process; you can invoke pwsh directly, with a script block:

pwsh -c { $SplatParms = $Args[0]; Invoke-RestMethod @SplatParms } -args $SplatParms

Note the need to pass the hashtable as an argument rather than as part of the script block.

  • Unfortunately, as of Windows PowerShell 5.1 / PowerShell Core 6.0.0, there is a problem with passing [PSCredential] instances this way - see bottom for a workaround.

This will execute synchronously and even print the output from the script block in the console.

The caveat is that capturing such output - either by assigning to a variable or redirecting to a file - can fail if instances of types that aren't available in the calling session are returned.

As a suboptimal workaround, you can use -o Text (-OutputFormat Text) thanks, PetSerAl to capture the output as text, exactly as it would print to the console(run pwsh -h to see all options).

Output is by default returned in serialized CLIXML format, and the calling PowerShell sessions deserializes that back into objects. If the type of a serialized object is not recognized, an error occurs.

A simple example (execute from Windows PowerShell):

# This FAILS, but you can add `-o text` to capture the output as text.WinPS> $output = pwsh -c { $PSVersionTable.PSVersion } # !! FAILSpwsh : Cannot process the XML from the 'Output' stream of 'C:\Program Files\PowerShell\6.0.0\pwsh.exe': SemanticVersion XML tag is not recognized. Line 1, position 82....

This fails, because $PSVersionTable.PSVersion is of type [System.Management.Automation.SemanticVersion] in PowerShell Core, which is a type not available in Windows PowerShell as of v5.1 (in Windows PowerShell, the same property's type is [System.Version]).


Workaround for the inability to pass a [PSCredential] instance:

pwsh -c {           $SplatParms = $Args[0];          $SplatParams.Credential = [pscredential] $SplatParams.Credential;          Invoke-RestMethod @SplatParms        } -args $SplatParms

Calling another PowerShell instance from within PowerShell using a script block involves serialization and deserialization of objects in CLIXML format, as also used in PowerShell remoting.

Generally, there are many .NET types that deserialization cannot faithfully recreate and in such cases creates [PSCustomObject] instances that emulate instances of the original type, with the (normally hidden) .pstypenames property reflecting the original type name prefixed with Deserialized.

As of Windows PowerShell 5.1 / PowerShell Core 6.0.0, this also happens with instances of [pscredential] ([System.Management.Automation.PSCredential]), which prevents their direct use in the target session - see this GitHub issue.

Fortunately, however, simply casting the deserialized object back to [pscredential] seems to work.


Try creating a New-PSSession against 6.0 within your 5.1 session.

After installing powershell core 6.0 and running Enable-PSRemoting, a new PSSessionConfiguration was created for 6.0:

PS > Get-PSSessionConfigurationName          : microsoft.powershellPSVersion     : 5.1StartupScript : RunAsUser     : Permission    : NT AUTHORITY\INTERACTIVE AccessAllowed, BUILTIN\Administrators AccessAllowed, BUILTIN\Remote Management Users AccessAllowedName          : microsoft.powershell.workflowPSVersion     : 5.1StartupScript : RunAsUser     : Permission    : BUILTIN\Administrators AccessAllowed, BUILTIN\Remote Management Users AccessAllowedName          : microsoft.powershell32PSVersion     : 5.1StartupScript : RunAsUser     : Permission    : NT AUTHORITY\INTERACTIVE AccessAllowed, BUILTIN\Administrators AccessAllowed, BUILTIN\Remote Management Users AccessAllowedName          : PowerShell.v6.0.0PSVersion     : 6.0StartupScript : RunAsUser     : Permission    : NT AUTHORITY\INTERACTIVE AccessAllowed, BUILTIN\Administrators AccessAllowed, BUILTIN\Remote Management Users AccessAllowed

In your parent script, create new session using the 6.0 configuration name PowerShell.v6.0.0 and pass it to any subsequent Invoke-Command you require. Results are returned as objects. Scriptblocks may require local variables passed through -ArgumentList, per mklement0's answer.

$ps6sess = New-PSSession -ComputerName localhost -ConfigurationName 'PowerShell.v6.0.0'$results = Invoke-Command -Session $ps60sess -ScriptBlock {Param($splatthis) Invoke-WebRequest @splatthis} -ArgumentList $SplatParms

It may also be useful to know that the session persists between Invoke-Command calls. For example, any new variables you create will be accessible in subsequent calls within that session:

PS > Invoke-Command -Session $ps60sess -ScriptBlock {$something = 'zers'}PS > Invoke-Command -Session $ps60sess -ScriptBlock {write-host $something }zers

Trouble with PSCredential passing doesn't seem to be a problem with this approach:

$ps6sess = New-PSSession -ComputerName localhost -ConfigurationName 'PowerShell.v6.0.0'$credential = Get-Credential -UserName 'TestUser'$IRestArgs = @{    Method='GET'    URI = 'https://httpbin.org'    Credential = $credential}$IRestBlock = {Param($splatval) Invoke-RestMethod @splatval}Invoke-Command -Session $ps6sess -ScriptBlock $IRestBlock -ArgumentList $IRestArgs# no errorpwsh -c {       Param ($SplatParms)      #$SplatParams.Credential = [pscredential] $SplatParams.Credential;      Invoke-RestMethod @SplatParms} -args $IRestArgs# error - pwsh : cannot process argument transformation on #   parameter 'Credential. username

Perhaps at the ps6 session knows it is receiving a block from ps5.1 and knows how to accommodate.


An immediate flash using start-process isn't proper, would have to research for that, regardless, my reaction is to construct an array of params to use not splat them for a try.