Lars Grünheid
Lars Grünheid

Reputation: 35

Powershell Core: Enter Powershell 5.1 Session

I have scripts that need PowerShell Core, but also need to use some ancient modules Microsoft won't bother to update for PowerShell Core (ActiveDirectory/GroupPolicy specifically), so it also needs to run some commands in PowerShell 5.1. Import-Module -UseWindowsPowerShell doesn't cut it, as it makes most information i need unaccessable through serialization.

So my idea was, use PSRemoting to make a new session running 5.1, and connect to this. Should be no problem using a session configuration that's targeting 5.1, right?

I quickly found out that there is no session configuration for 5.1 under Core by default. So i grabbed the default configuration for a 5.1 Session and tried to register it under Core:

$registerPSSessionConfigurationSplat = @{
    "RunAsPassword" = ""
    "ResourceUri" = "http://schemas.microsoft.com/powershell/microsoft.powershell"
    "Capability" = @("Shell")
    "PSVersion" = "5.1"
    "AutoRestart" = $false
    "ExactMatch" = $true
    "RunAsVirtualAccount" = $false
    "SDKVersion" = "2"
    "Uri" = "http://schemas.microsoft.com/powershell/microsoft.powershell"
    "MaxConcurrentCommandsPerShell" = "2147483647"
    "IdleTimeoutms" = "7200000"
    "ParentResourceUri" = "http://schemas.microsoft.com/powershell/microsoft.powershell"
    "RunAsUser" = ""
    "OutputBufferingMode" = "Block"
    "Architecture" = "64"
    "MaxMemoryPerShellMB" = "2147483647"
    "MaxProcessesPerShell" = "2147483647"
    "Filename" = "%windir%\\system32\\pwrshplugin.dll"
    "MaxShellsPerUser" = "2147483647"
    "MaxShells" = "2147483647"
    "SupportsOptions" = $true
    "lang" = "de-DE"
    "MaxIdleTimeoutms" = "2147483647"
    "xmlns" = "http://schemas.microsoft.com/wbem/wsman/1/config/PluginConfiguration"
    "Enabled" = $true
    "SecurityDescriptorSddl" = "O:NSG:BAD:P(A;;GA;;;BA)(A;;GA;;;IU)(A;;GA;;;RM)S:P(AU;FA;GA;;;WD)(AU;SA;GXGW;;;WD)"
    "Name" = "Powershell.5.1"
    "ProcessIdleTimeoutSec" = "0"
    "MaxConcurrentUsers" = "2147483647"
    "UseSharedProcess" = $false
    "RunAsVirtualAccountGroups" = ""
    "XmlRenderingType" = "text"
    "Permission" = "NT-AUTORITÄT\\INTERAKTIV AccessAllowed", "VORDEFINIERT\\Administratoren AccessAllowed", "VORDEFINIERT\\Remoteverwaltungsbenutzer AccessAllowed"
}
Register-PSSessionConfiguration @registerPSSessionConfigurationSplat

It came back with this:

Cannot bind parameter 'PSVersion' to the target. Exception setting "PSVersion": "PowerShell remoting endpoint versioning is not supported on PowerShell Core.

Yea, thanks for nothing. I am out of ideas. What are my options to achieve my goal?

I have a workaround, sort of, by calling powershell.exe with the 5.1 specific code and parse the output, but that's likely prone to several formatting issues that might occur, and i would really like to avoid those.

Upvotes: 1

Views: 78

Answers (1)

mklement0
mklement0

Reputation: 439777

If, from a PowerShell (Core) 7 session, you're looking to run code in a Windows PowerShell session locally, use the Windows PowerShell Compatibility feature:

  • Create a Windows PowerShell session with New-PSSession -UseWindowsPowerShell.

  • Pass this session to Invoke-Command -Session along with a script block containing statement(s) to be executed in the Windows PowerShell session.

Caveat:

  • The statements executing inside the Windows PowerShell session support full type fidelity among them.

  • However, what the script block outputs, i.e. what is seen by the PowerShell 7 caller is subject to the usual type-fidelity limitations that affect any form of cross-process serialization in PowerShell. See this answer for details.

A simple example:

# ->, e.g.: '5.1.26100.2161'
Invoke-Command -Session ($winPsSess = New-PSSession -UseWindowsPowerShell) {
  # Place all statements you want to be executed by *Windows PowerShell*
  # here.
  [string] $PSVersionTable.PSVersion
}

Note:

  • New-PSSession -UseWindowsPowerShell creates a hidden powershell.exe process that hosts a Windows PowerShell session and which the caller communicates with in a manner similar to running a background job.

    • Notably, this is different from creating a new - bona fide, "loopback" - remoting session, which invoking New-PSSession without arguments would create, which uses the WS-MAN network transport instead of directly communicating with a hidden background process (of necessity on the same machine).

    • Notably, the latter - unlike when you use -UseWindowsPowerShell - only works when invoked from an elevated (run-as-admin) process (see the bottom section for more information).

  • In the event that you need to conserve resources, you can remove the Windows PowerShell session later with Remove-PSSession $winPsSess


As for your later comments:

as i said, the Configuration "microsoft.powershell" doesn't exist for me on Core.

While Get-PSSessionConfiguration (which must be run from an elevated session) reports available remoting end-point configurations, only in Windows PowerShell does it report the configurations for both PowerShell editions:

  • By contrast, in PowerShell (Core) 7 it only reports the configurations for that edition, which explains why the Windows PowerShell-specific microsoft.powershell configuration didn't show up.

once i figure out why Core isn't the default endpoint for remote sessions

Indeed, using PowerShell remoting as of PowerShell (Core) 7.5.0 still targets Windows PowerShell on remote machines by default, unless overridden via a -ConfigurationName argument on the client side or via the $PSSessionConfigurationName preference variable.

  • See this answer for details.

  • GitHub issue #11616 suggests changing the default to targeting PowerShell (Core) 7 on remote machines for PowerShell (Core) 7 callers.


Optional reading: Why you should prefer the Windows PowerShell Compatibility feature to using PowerShell remoting for running Windows PowerShell code locally from a PowerShell 7 session:

Note: I'm hazy on the lower-level details, but I'm hoping the big picture I'm painting is correct.

  • New-PSSession -UseWindowsPowerShell (i.e. creation of a session via the Windows PowerShell Compatibility feature) uses local IPC (inter-process communication) to communicate directly with a hidden (background) powershell.exe process, i.e. with a Windows PowerShell process; specifically, named pipes are used.

  • By contrast, New-PSSession (i.e. creation of a "loopback" remoting session to the local machine) uses the networking infrastructure to communicate with Windows PowerShell; specifically, it uses a WS-MAN-based network transport, in the form of Microsoft's implementation of this standard, WinRM.

    • Note: Apart from needing PowerShell remoting to be enabled on the local machine, the default (loopback) remoting endpoint is controlled via the $PSSessionConfigurationName preference variable, which defaults to
      http://schemas.microsoft.com/powershell/Microsoft.PowerShell.
      Since this variable may have been modified on a given machine, the robust form to predictably initiate a Windows PowerShell loopback remoting session is:
      New-PSSession -ConfigurationName microsoft.powershell

The upshot: New-PSSession -UseWindowsPowerShell (the Windows PowerShell Compatibility feature) is preferable to loopback remoting for several reasons:

  • PowerShell remoting needn't be enabled on a given machine.

  • You can run the code from a regular, non-elevated PowerShell 7 session, whereas use of loopback remoting requires elevation (running as admin); this means that your user account doesn't even need to be a member of the administrators group.

  • You don't incur the overhead from communicating via the network infrastructure - though that may not make a noticeable difference in practice.

Upvotes: 1

Related Questions