Last Update: Sep 04, 2024 | Published: Oct 01, 2020
With the advent of PowerShell Core and PowerShell 7, not all modules have been updated to be compatible with core engine updates or cross-platform accessibility. Instead of needing to switch between multiple versions of PowerShell depending on the tasks at hand, PowerShell 7 has added compatibility layers for existing Windows PowerShell compatible modules.
Of course, with any compatibility layer, there are always situations that will not work properly or unexpected behaviors. In this article, we explore how the Windows Compatibility layer works within PowerShell 7 and what you need to be aware of.
There are still many modules that have not been updated to work with PowerShell 7. This can be due to many different reasons, but generally, it’s due to one of the following.
Notably, with the change to .NET Core 3.1, there are many breaking changes that .NET Framework (4.x) has and may prevent modules from working correctly with .NET Core and therefore PowerShell 7. Additionally, there are core engine changes and Windows-specific APIs that a module may need to update to work properly with PowerShell 7.
One big note here is that these compatibility layers are Windows-specific, and do not apply to the cross-platform nature and ability of PowerShell 7. That is not to say that some modules won’t function if they utilize remoting to operate on a remote Windows system, but in this article, we are discussing locally loaded modules needing compatibility.
With all that discussion how does the Windows Compatibility layer work? The modules are not loaded into the current PowerShell 7 runtime. In the background, Windows PowerShell is running a version of the imported module. Any data sent to and from the module is then serialized and exposed to the PowerShell 7 session. The actual process follows the steps shown below:
WinPSCompatSession
is created to Windows PowerShell 5.1.$env:Temp
folder.WinPSCompatSession
is removed, destroy the session.In case you may not remember, there are two types of remoting within PowerShell. Implicit and Explicit remoting. Explicit remoting is when you create a session and then consume that session intentionally to run remote commands. Implicit remoting is the act of running a module that then creates a proxy function to send the commands to an automatically created remote session and return the results.
That brings us to our next point, what is a proxy module? A proxy module is a script function that takes command input on a local computer and sends that over to a secondary session, local or remote, to run a command. This allows the local PowerShell session to utilize commands that it may not have installed.
There are a few ways to configure how the Windows Capability layer works in PowerShell 7. In PowerShell 7 this is configured via the powershell.config.json
file. This file can live in one of two different locations.
$PSHOME
location is defined as the same directory as the executing System.Management.Automation.dll
assembly. For example, on a typical PowerShell 7 install, this is located here: C:\Program Files\PowerShell\7
Split-Path $PROFILE.CurrentUserCurrentHost
. Typically this is located in the user profile here: C:\Users\username\PowerShell
Once you locate the configuration file to modify there are a few commands that we can use to customize how exactly Windows PowerShell modules are loaded.
By modifying the WindowsPowerShellCompatibilityModuleDenyList
setting, you can make sure that certain modules are excluded from being able to be run using the compatibility layer. Below are the default modules excluded from loading.
"WindowsPowerShellCompatibilityModuleDenyList": [
"PSScheduledJob","BestPractices","UpdateServices"
]
If you want to disable the ability to implicity load Windows PowerShell modules that are not present in your PowerShell 7 loading, you can use the DisableImplicitWinCompat
setting. If set to true, this command will force the explicit loading of modules.
With explicit loading, there is the possibility of overwriting native PowerShell 7 modules with those of Windows PowerShell. Though not fully released yet, in PowerShell 7.1 this behavior has been modified to disallow clobbering the following core modules:
To avoid clobbering additional modules, you can also add them to the PowerShell 7 config setting, WindowsPowerShellCompatibilityNoClobberModuleList
. The example is seen below to exclude the AzureAD
module from being clobbered. Keep in mind that this only works in PowerShell 7.
"WindowsPowerShellCompatibilityNoClobberModuleList": ['AzureAD']
There are two examples of loading that we are going to take a look at here. Implicit loading and explicit loading.
A common example of explicit loading is that of the AzureAD
module. Due to certain issues with loading the module in PowerShell 7, it is sometimes necessary to load this module via Windows PowerShell in the background. To do this, simply use the -UseWindowsPowerShell
switch when loading.
Import-Module -Name 'AzureAD' -UseWindowsPowerShell
To take advantage of implicit remoting, you can simply try loading the ServerManager
module. As you can see in the image below, not only is the module automatically loaded using the compatibility layer, PowerShell 7 displays a warning regarding the serialization of results.
Import-Module -Name 'ServerManager'
Get-Module -Name 'ServerManager'
With all the changes introduced by PowerShell 7, modules naturally broke, but with the introduction of a compatibility layer, many of the issues with co-existing versions of PowerShell are alleviated. The utility and power of the Windows PowerShell Compatibility Layer continue to evolve over time and soon will cover nearly every use case. This is until such time that all modules are converted to PowerShell 7 native modules.