PowerShell 7 Windows module compatibility

PowerShell 7 is finally here (woot!) and it’s time for Windows users to think seriously about migrating to pwsh 7. One obstacle in doing so in the PowerShell Core (6.x) release was limited compatibility with Windows PowerShell 5.1 modules. There are some Azure modules — my poster child is AzureAD — that an Azure architect can’t live without and which cling stubbornly to Windows PowerShell 5.1. Over time, things will get better (I hope) but the PowerShell team decided to do something in the meantime to ease Windows users into migrating to PowerShell 7.

I think what they released will prove to be quite useful: PowerShell 7 does provide a a way to use 5.1-only modules in PowerShell 7. It works only on a Windows desktop or server (no help for us macOS and Ubuntu fans) and has some very interesting attributes you should know about.

Here’s the good news. It works and is easy to use. In this gif, you see a simple script that assigns Get-AzureAdTenantDetail output to a variable after a Connect-AzureAD cmdlet. This is running in pwsh 7, as you can see on the status bar lower right of the window. If you watch carefully, you’ll see how it’s done: via a remote PowerShell session to localhost. You can clearly see the remote session being set up.

PowerShell 7 loads Windows PowerShell (desktop) modules in a remote session
(click to enlarge)

To use a Windows PowerShell 5.1 module in pwsh 7, you Import-Module using a new parameter: UseWindowsPowerShell. The rest is automatic. Let’s start by taking a look at that remote session to localhost. Aside from its name —WinPsCompatSession— it looks just like any remote PowerShell session. Pretty clever.

PowerShell 7 WinCompatSession is automatically created for Windows PowerShell modules
(click to enlarge)

Did you notice that message about objects being deserialized in the gif as the module import proceeds? What’s that about? It turns out that the object returned from a module run in pwsh 7 with UseWindowsPowerShell is not the same as the object returned when you run a cmdlet natively in Windows PowerShell 5.1. There are some subtle but important differences. This screenshot compares the output of Get-Member for the same cmdlet in 5.1 and in 7 using the compatibility feature.

Objects returned in Windows PowerShell 5.1 compatibility mode are deserialized (click to enlarge)

The pwsh 7 object is “deserialized”. I was mystified by this until I found this ten-year-old blog post by the PowerShell team which neatly details why this is happening. Bottom line — methods on an object are (usually) not available in pwsh 7 when the module was loaded with UseWindowsPowerShell. That’s because you can’t invoke the method “there” (in the PowerShell 5.1 session started to support the legacy module) from “here” (your pwsh 7 session). This is the case even thought the remote session is on the local machine. There are some exceptions; see the blog post for details.

Practically speaking, I don’t think this matters much unless there is a specific method you need from an object you can only get from a 5.1 module. In that case, you’ll have to use PowerShell 5.1. But since most (or all) of the properties are available in the deserialized object that’s returned to pwsh 7, you can still code logic based on values and/or returned states. Personally, this will work for me for the vast majority of things that I use legacy PowerShell 5.1 modules for.

As always, there’s an About_ page you should spend some time looking over to fully understand the new compatibility functions.

Now, if the PowerShell team would just containerize the Windows PowerShell 5.1 remote session and let me 5.1 load modules on macOS… Well, I can dream, right?





4 responses to “PowerShell 7 Windows module compatibility”

  1. Reza Avatar

    I just wanted to add for future readers that there is always another (older) alternative to this approach and that is ‘Invoke-WinCommand’ that allows you to run a piece of script there (in PowerShell 5.1 aka PowerShell Deskstop).

  2. Michael Viglianco Avatar
    Michael Viglianco

    I am new to PS. When trying to register a new application using New-AzureADApplication in PowerShell 7 I am unable to pass a -RequiredResourceAccess object to the cmdlet when imported with -UseWindowsPowershell. I get an error mentioning thaat Deserialized.Microsoft.Open.AzureAD.Model.RequiredResourceAccess cannot be converted to Microsoft.Open.AzureAD.Model.RequiredResourceAccess. I assume these are related issues. I have found a work around using graph but I’d love to know if there is a way to make this work.

    1. Alex Neihaus Avatar
      Alex Neihaus

      Hi, Michael.

      I think the answer you question is in the error message. The deserialized object isn’t what New-AzureAdApplication expects. It may look the same but it’s not.

    2. Alex Neihaus Avatar
      Alex Neihaus

      Here’s another idea, instead of struggling with a module that doesn’t run in pwsh. Try New-AzAdApplication.

Leave a Reply

Your email address will not be published. Required fields are marked *