Chapter 2
PowerShell 7 Compatibility with Windows PowerShell

PowerShell 7, like Windows PowerShell, is a .NET application—an application built on top of and leveraging the .NET Framework, particularly the base class libraries.

Microsoft built Windows PowerShell using the full .NET Framework employing a design in which cmdlets are a thin and intelligent layer above the .NET Framework. Cmdlets rely on the .NET Framework to do all the heavy lifting. Windows PowerShell 5.1 leverages the Microsoft .NET Framework version 4.5.2.

PowerShell 7 is a complete and open source reimplementation of Windows PowerShell based on the open source .NET Core Framework using .NET Core 3.1. This has been a huge reengineering job and one that has thrown up a few challenges.

In PowerShell, commands are contained within modules. To run any command, PowerShell must first load a module that contains that command. PowerShell can only load modules that contain cmdlets and other binary artifacts if their developer has enabled this.

The PowerShell team reimplemented most of the commands contained in the core PowerShell convert to modules and ensured that the Active Directory module works with PowerShell 7.

Some Microsoft product teams and other external teams have not yet ported their modules for you to use natively in PowerShell 7. To get around this issue, the PowerShell team developed a compatibility solution that enables most commands previously developed for Windows PowerShell to work in PowerShell 7.

The compatibility solution enables you to use PowerShell 7 to manage a wide range of features in Windows Server, as demonstrated throughout this book. That does leave, however, a small set of features, modules, and commands that you are not able to use with PowerShell 7. For those, there are work‐arounds.

This chapter looks at the following topics:

  • In “Examining PowerShell Modules,” you'll learn what the various modules contain and how they work.
  • In “Introducing the Compatibility Solution,” you'll see what the solution is and how you use it.
  • In “Things That Do Not Work with PowerShell 7,” you'll examine the Windows PowerShell features that do not work within PowerShell 7, with or without the compatibility solution. You'll also discover work‐arounds to issues caused by incompatibility.

System Used in This Chapter

In this chapter, you use one host, DC1. This is a Windows Server 2019 Datacenter Edition host.

Figure 2.1 shows the system you use in this chapter.

image

Figure 2.1: System used in this chapter

Examining PowerShell Modules

Before looking at compatibility with Windows PowerShell, you should understand PowerShell modules. Modules are fundamental to PowerShell, and understanding how they work is important when dealing with compatibility.

A PowerShell module is a package of commands. A module contains members, which can include cmdlets, providers, functions, variables, and aliases. Modules are the means that developers use to package and distribute PowerShell commands. You import a module into PowerShell to use the members of the module.

Understanding Module Types

There are four main module types in PowerShell. Each one is used differently and solves different problems.

  • Script modules: These modules are contained in a file with a .PSM1 extension and contain function definitions. When you import a script module, the functions defined in the .PSM1 files are imported into the current PowerShell session.
  • Manifest modules: These are modules that have a module manifest. A module manifest is a file with a .PSD1 extension that contains details about a module. This information includes metadata (author, copyright) and instructions on what the module contains and how PowerShell should load it.
  • Binary modules: At its simplest, a binary module is a .NET assembly containing commands stored in a DLL file. A cmdlet developer writes the cmdlet code in C# and compiles it into a DLL; at this point, you can load the assembly by using Import‐Module and specifying the DLL. To simplify loading of a binary module, you use a module manifest to help PowerShell load the needed members such as related help files.
  • Dynamic modules: These are dynamically created modules that PowerShell creates in memory from a script block you supply. Dynamic modules enable you to use a script to create a module on demand that does not need to be loaded or saved to persistent storage.

Any module can have additional members, although PowerShell needs to have a manifest to enable it to load those members. For example, you could include some XML for formatting or include a help file for the module.

Script modules were an easy way to distribute functions and replaced the practice of dot sourcing of .PS1 files, which was a common approach many users took with PowerShell version 1.

A manifest module is one that has a PowerShell module manifest file. This is a text file that contains the module details in the form of a hash table. With a module manifest, a developer can repackage a snap‐in into a binary module. You can create a module manifest using New‐ModuleManifest, as described at docs.microsoft.com/powershell/module/microsoft.powershell.core/new-modulemanifest.

You use dynamic modules to create a module on the fly based on the script block. As such, these are intended to be short‐lived, and you cannot look at them using Get‐Module. Dynamic modules do not require manifests.

To demonstrate the use of modules, you can create a simple module on DC1, as follows:

# 1. Create a simple script module—MyModule1
$MyModulePath = "C:\Users\$env:USERNAME\Documents\PowerShell\Modules\MyModule1"
$MyModule = @"
# MyModule1.PSM1
Function Get-HelloWorld {
  "Hello World from My Module"
}
"@
New-Item  -Path $MyModulePath -ItemType Directory -Force | Out-Null
$MyModule | Out-File -FilePath $MyModulePath\MyModule1.PSM1
Get-Module—Name MyModule1 -ListAvailable

You can see the output from these commands in Figure 2.2.

image

Figure 2.2: Creating a new module

These commands create a new module, MyModule1, which is a simple script module created as a .PSM1 file. You can see in the output that this module exports a single command, the Get‐HelloWorld function.

Importing PowerShell Modules

There are three ways PowerShell imports a module into the current PowerShell session. First, you can use the Import‐Module cmdlet to import a module explicitly. Second, you can use the module autoload feature, introduced with PowerShell version 2. Finally, if you use Get‐Command to discover the commands within a module, PowerShell ensures the module is imported and then returns the requested commands.

Using Import‐Module allows you to load any module from any location. You can load just a single .PSM1 file, a single .NET assembly, or a rich multimember module with the help of a module manifest.

With module autoload, when you use any command that is in a module you have not yet imported, PowerShell loads the module and then runs the command. This also loads any members of the module, such as a help file. You can turn this off by setting the PowerShell environment variable $PSModuleAutoloadingPreference to none.

You can use Import‐Module to import the MyModule1 module you just created.

# 2. Import the MyModule1 module
Import-Module -Name MyModule1 -Verbose

You can see the output from these commands in Figure 2.3.

image

Figure 2.3: Importing the MyModule1 module

You could also use module autoload by just specifying the Get‐HelloWorld function.

Using PowerShell Module Manifests

A module manifest in PowerShell is a file with a .PSD1 extension that contains a hash table of values. These values, which are held in a PowerShell hash table, include module metadata, such as the author, plus other information to enable PowerShell to load the module.

The article at docs.microsoft.com/powershell/scripting/developer/module/how-to-write-a-powershell-module-manifest provides more information about manifests and describes how to write one.

You can add a manifest to the MyModule1 module as follows:

# 3. Create and Test a new module manifest
$NMMFHT = @{
  Path        = "$MyModulePath\MyModule1.PSD1"
  Author      = "Thomas Lee"
  CompanyName = 'PS Partnership'
  Rootmodule  = 'MyModule1.psm1'
}
New-ModuleManifest @NMMFHT
Get-Module -Name MyModule1 -List
# remove and re-import
Get-Module -Name MyModule1 | Remove-Module
Import-Module -Name MyModule1 -Verbose
Get-HelloWorld

These commands first create a new module manifest and review the module after you create the manifest. Then you import the module explicitly and finally run the Get‐HelloWorld function, as you can see in Figure 2.4.

image

Figure 2.4: Creating and using a manifest

You can view the hash table by viewing MyModule1.PSD1, or you can use your favorite code editor to update it as necessary.

Module Naming

In general, a module is a folder that contains one or more members. The simplest module is a .PSM1 file with function definitions. In this case, you have a folder, perhaps called MyModule. Below this folder you have a file, MyModule.PSM1. PowerShell requires the folder name and the .PSM1 filename to be the same; otherwise, Import‐Module does not import the module.

You can convert a script module into a manifest module by adding a .PSD1 file to the module folder. In this case, the .PSD1 file must have the same name as the module folder. In such a case, the manifest contains the filename of the .PSM1 file. The filenames can be different but should be the same to avoid confusion.

After you have created MyModule1, you can view the files contained in the module using Windows Explorer. In Figure 2.5, you can see the module, with both the .PSM1 and .PSD1 files.

image

Figure 2.5: Viewing files in MyModule1

PowerShell supports module versioning, which allows you to have multiple versions of a module on a host. You use Import‐Module to load a specific version or let it take the default of loading the latest version. For example, you could have both versions 1.0.0 and 1.1.0 of MyModule. By default, Import‐Module (and module autoload) would load the latest version, which is 1.1.0. You can specify the earlier module as needed.

Creating a Module with Multiple Versions

You can create and use a module with multiple versions with the following code:

# 4. Create MyModule2 with 2 versions
# Create Module folders
$MyModule2Path   =
  "$env:USERPROFILE\Documents\PowerShell\Modules\MyModule2"
$MyModule2V1Path = "$MyModule2Path\1.0.0"
$MyModule2V2Path = "$MyModule2Path\2.0.0"
New-Item -Path $MyModule2Path -ItemType Directory -Force | Out-Null
New-Item -Path $MyModule2Path -Name '1.0.0' -ItemType Directory -Force |
  Out-Null
New-Item -Path $MyModule2Path -Name '2.0.0' -ItemType Directory -Force |
  Out-Null
# Create MyModule2V1.PSM1
$MyModule2V1 = @"
Function Get-HelloWorld2 {
  "Hello World from MyModule2 (V1)"
}
"@
$MyModule2V1 | Out-File -Path "$MyModule2V1Path\MyModule2.PSM1"
# Create MyModule2V2.PSM1
$MyModule2V2 = @"
Function Get-HelloWorld2 {
  "Hello World from MyModule2 (V2)"
}
"@
$MyModule2V2 | Out-File -Path "$MyModule2V2Path\MyModule2.PSM1"
# Create manifests for both versions of this module
$NMMFHV1HT = @{
  Path        = "$MyModule2V1Path\MyModule2.PSD1"
  Author      = "Thomas Lee"
  CompanyName = 'PS Partnership'
  Rootmodule  = 'MyModule2.psm1'
}
New-ModuleManifest @NMMFHV1HT -ModuleVersion '1.0.0'
$NMMFHV2HT = @{
  Path        = "$MyModule2V2Path\MyModule2.PSD1"
  Author      = "Thomas Lee"
  CompanyName = 'PS Partnership'
  Rootmodule  = 'MyModule2.psm1'
}
New-ModuleManifest @NMMFHV2HT -ModuleVersion '2.0.0'

These commands create the module folders and create the module itself.

Using Module Versions

In the previous section, you created a new module ( MyModule2) with two versions. You can use the functions within the different versions of a module as follows:

# 5. Use MyModule2
# Discover, import and use MyModule2
Get-Module MyModule2 -ListAvailable
Import-Module -Name MyModule2 -Verbose -RequiredVersion '1.0.0'
Get-HelloWorld2
# Re-import MyModule2—by default the highest version
Import-Module -Name MyModule2 -Force -Verbose
# Use V2 Function
Get-HelloWorld2

You can see the results of these commands in Figure 2.6.

image

Figure 2.6: Using MyModule2

In this code snippet, you use Get‐Module to discover MyModule2. You then load version 1.0.0 of MyModule2. If you use the ‐Verbose switch, PowerShell displays the process of importing your module. Verbose output like this can be useful in debugging more complex modules. With version 1 of MyModule2 loaded, you run the first version of the Get‐HelloWorld2 function, showing the expected version 1 output. You then remove the old module explicitly and reimport the MyModule2 module and use the function. You can see that because you did not specify a version, PowerShell loads the highest version of the function, which then returns the expected version 2 output.

You can have a simple module with just a .PSM1 file. However, you must use a module manifest if you want to support multiple versions of the same module. In that case, the folder containing a given version must match the corresponding version number contained in the manifest. You can see the structure of MyModule2 in the filestore in Figure 2.7.

image

Figure 2.7: Viewing MyModule2

Using Module Autoload

With module autoload, PowerShell loads the module that contains the command you are using before running the command. PowerShell uses the environment variable PSModulePath to hold a comma‐separated value list of paths in the Windows filesystem. PowerShell can discover the commands in any module. You can view this variable by typing $Env:PSModulePath.

In “Module Naming,” you created MyModule2, with two versions of the module. To demonstrate autoload, you can do the following:

# 6. Demonstrate autoload of MyModule2
Get-Module MyModule* | Remove-Module -Verbose
Get-HelloWorld2

In these commands you first remove all the MyModule modules and then, with no modules loaded, use a command in the module MyModule2, namely, Get‐HelloWorld2, provided for test purposes like this. You can see the output in Figure 2.8.

You can modify the module path environment variable(s) externally to PowerShell by using the sysdm.cpl applet. If you do so, the next time you enter PowerShell, you should see the updated environment variable value.

Windows supports two environment variables (of the same name)—one for the user and one for the system. PowerShell adds the values contained in the user and system Windows environment variables into the value $Env:PSModulePath. You can define both the system‐ and user‐level PSModulePath variables using sysdm.cpl. In most cases, the module autoload path is sufficient.

image

Figure 2.8: Using module autoload

Third‐party publishers ship installers that install their module(s) into a module‐specific location by extending the PSModulePath variable.

PowerShell does not persist any changes you make to the value of $Env:PSModulePath inside a PowerShell session. You can make any changes to your profile scripts if necessary.

Viewing the Module Analysis Cache

When PowerShell uses autoloading, it has a large number of modules to search to discover which module has the requested command. To improve performance, PowerShell maintains an internal module analysis cache of all the modules on your system and the commands they contain. By default, PowerShell stores this cache at $Env:LOCALAPPDATA\Microsoft\Windows\PowerShell\ModuleAnalysisCache. You can view the cache file using Get‐ChildItem.

# 7. View Module Analysis Cache
$CF = "$Env:LOCALAPPDATA\Microsoft\Windows\PowerShell\"+
      'ModuleAnalysisCache'
Get-ChildItem -Path $CF

Figure 2.9 shows the output and the file containing the cache.

image

Figure 2.9: Viewing the module analysis cache

At the start of each session, PowerShell spawns a low‐priority background thread that discovers the modules and the commands and updates the cache file accordingly. To learn more about the module analysis cache, see:

docs.microsoft.com/powershell/module/microsoft.powershell.core/about/about_windows_powershell_5.1?view=powershell-5.1#module-analysis-cache.

In most cases, your module analysis cache remains constant, since in most cases, you rarely change the modules on your system. Should you want, you can change the location of the cache file. There is really little reason to do so, however, and it could just introduce more troubleshooting challenges.

Introducing the Compatibility Solution

Windows PowerShell was first launched in 2006 and has been a built‐in component in Windows for more than a decade. The first two releases of what is now PowerShell 7 were known as PowerShell Core, with the emphasis on Core as in .NET Core. The development team released two versions (6.0 and 6.1). As part of the planning for the third major version of PowerShell, Microsoft decided to rename the product PowerShell 7, dropping the Core moniker. This book focuses on PowerShell 7.

With the first version PowerShell Core, it became clear that some Windows PowerShell modules did not work in the version of PowerShell based on .NET Core. An early solution to this issue was to use explicit remoting. You could create a PowerShell remoting session to your host using a PowerShell 5.1 remoting endpoint and then run your commands in that remoting session. That method works, but it means you have to manage the process and so is not ideal.

Implicit remoting is a feature of PowerShell that lets you use commands that are not available in locally installed modules. Exchange Server uses implicit remoting, and this is a handy solution cross‐platform. It could be used, for example, if you want to run AD commands from a Mac or Linux computer.

In PowerShell 7, whenever Import‐Module attempts to import any module, it looks to see whether that module is compatible with PowerShell 7. If the module is compatible, then Import‐Module loads it into PowerShell 7, but if not, then PowerShell loads the module in compatibility mode.

When Import‐Module imports a module in compatibility mode, it creates a PowerShell remoting session to the local host using a Windows PowerShell remoting endpoint, named WinPSCompatSession. PowerShell then imports the module into the remote session running Windows PowerShell and finally uses Import‐PSSession to import the commands into the current PowerShell 7 session. This creates functions that duplicate the names of the commands imported from the remote session. Those functions use implicit remoting to run the actual cmdlet logic in the remote session.

You can view the use of the compatibility solution as follows:

# 8. Import Server Manager Module on DC1 and use it
Get-Module ServerManager -ListAvailable
Import-Module ServerManager
Get-Module ServerManager | Format-Table -AutoSize -Wrap
Get-WindowsFeature -Name Hyper-V | Format-Table -AutoSize
$CS = Get-PSSession -Name WinPSCompatSession
Invoke-Command -Session $CS -ScriptBlock {
  Get-WindowsFeature -Name Hyper-V | Format-Table -AutoSize
}

You can see the output from these commands in Figure 2.10.

image

Figure 2.10: Loading the ServerManager module

In the output, you can see that when you first use Get‐Module to find the ServerManager module, you see no output, which is expected because you have not yet loaded the module. You can then manually import the ServerManager module and review it. As you can see, this is a script module. Finally, you use a command to view a windows feature. The first time you attempt to view the Hyper‐V feature, you can see that the normal DisplayName field is missing from the output. This is because Import‐Module has not also imported the module's display XML into the PowerShell 7 session. As you can see, you can get the output by using the compatibility remoting session and having PowerShell do the formatting in that remote session.

If your script uses Import‐Module to enable you to access commands from multiple modules, Import‐Module checks to see whether WinPSCompatSession exists. If so, PowerShell uses the existing session to autoload additional modules. This means you only ever have one compatibility remoting session. You can read more about implicit remoting and Import‐PSSession at docs.microsoft.com/powershell/module/microsoft.powershell.utility/import-pssession.

Using the Module Load Deny List

During the development of PowerShell 7, it became clear that a small number of modules would never work either natively or via the compatibility solution. If you did attempt to use them in the compatibility session, commands in the modules failed. The failure usually resulted in error messages that were unclear and not actionable. This was not a good user experience, particularly to new users.

To avoid that bad experience, PowerShell 7 has a list of modules that Import‐Module does not load (natively or via the compatibility solution), by default. You can override this logic by using Import‐Module with the ‐SkipEditionCheck parameter, although that is unlikely to be successful.

In the PowerShell installation folder, you can find PowerShell's configuration file $PSHOME\PowerShell.config.json. You can view the contents of this file, as follows:

# 8. View JSON Configuration File on DC1
Get-Content -Path $PSHOME\powershell.config.json

You can see the output of this command in Figure 2.11.

image

Figure 2.11: Viewing the module deny list

This configuration file, which you can change as needed, contains a list of modules that Import‐Module does not load. These are based on module names, so if the product team updates these modules so that they become usable, you can change the file. Also, if and when those denied modules do change and you can use them in PowerShell 7, PowerShell may update the configuration file at the next PowerShell update.

Things That Do Not Work with PowerShell 7

Thanks to the great work by a combination of the PowerShell 7 product team and the PowerShell community, the majority of Windows PowerShell commands function properly in PowerShell 7. This means your Windows PowerShell scripts should run just fine in PowerShell 7, as this book amply demonstrates.

The compatibility solution does impose minor limitations caused by the serialization of data between the PowerShell 7 session and the compatibility remoting session. When you transfer data via remoting, PowerShell serializes the data into XML, transports the data, and then deserializes it at the other end. When a command receives commands from the remote session, that data is deserialized. This means that there are no object methods (aside from a few default ones that all objects have) returned. This is why, for example, the UpdateServices module does not work—it relies on object methods instead of cmdlets. Also, PowerShell changes the object type name to reflect the serialization.

Despite those limitations, almost all the modules supported by the compatibility solution work. (That is, the commands function and do their jobs.) That means you should be able to run Windows PowerShell scripts in PowerShell 7 successfully—as this book more than adequately demonstrates. But there are some features, modules, and commands PowerShell 7 does not support either natively or via the compatibility solution.

Windows PowerShell Incompatibilities

Despite a lot of hard work by the PowerShell team and others, there remains a small set of PowerShell 5.1 and earlier features, modules, and cmdlets that simply do not work with PowerShell 7, with or without the compatibility solution. These include the following:

  • PowerShell workflows
  • PowerShell snap‐ins
  • WMI cmdlets
  • The ‐ComputerName parameter on some cmdlets
  • Desired State Configuration (DSC)
  • Windows Server Update Services (WSUS)
  • The Best Practices module
  • The WebAdministration module IIS provider
  • The Add‐Computer, Checkpoint‐Computer, Remove‐Computer, and Restore‐Computer commands from the Microsoft.PowerShell.Management module

PowerShell workflows were based on the Windows Workflow Framework component of the full NET Framework. The .NET Core team did not choose to implement the necessary components to support workflows in .NET Core. The workflow feature was not heavily used, and the PowerShell team decided not to carry it forward. If you are using workflows, you can either continue to use them by using Windows PowerShell or look into alternatives.

One use of workflows was to improve performance using the Workflow component's built‐in parallelism. With the implementation of Foreach‐Object‐Parallel, you can get the needed parallelism (and improve script run times) without using Windows PowerShell workflows.

It is not likely that workflows are going to be implemented with PowerShell 7. See the release notes at docs.microsoft.com/en-us/powershell/scripting/whats-new/breaking-changes-ps6?view=powershell-6.

PowerShell 7 does not support Windows PowerShell snap‐ins. In Windows PowerShell V1, you used snap‐ins to hold commands, but this approach lacked flexibility. With snap‐ins, you had to use a compiled language such as C# to write your command—you could not write your commands using PowerShell. The developer also needed to create an installer program (although there is a default installation program included with .NET). Also, the installer stored details of the module in a protected area of the registry, meaning you needed administrative permissions to install a module.

The module feature, which was added in Windows PowerShell 2, in effect replaced the snap‐in. Snap‐ins continue to be supported in Windows PowerShell. In some cases, you may be able to convert a snap‐in into a module by using a manifest. For other snap‐ins, you may need to ask your internal developer or external vendor to update their product (or seek alternative solutions that support PowerShell 7).

The WMI cmdlets are not supported in PowerShell 7. You can, and should, use the CIM cmdlets. The CIM cmdlets are lighter weight and provide improved usability and reduced network bandwidth. Although the article is old, you can read more about the CIM cmdlets at devblogs.microsoft.com/powershell/introduction-to-cim-cmdlets /.

Some Windows PowerShell commands contain the ‐ComputerName parameter. For example, you can specify a value of that parameter to Get‐Service to have the cmdlet get services on the specified machine. In PowerShell 7, the following commands do not support the ‐ComputerName parameter:

  • Clear‐EventLog
  • Get‐Process
  • Get‐Service
  • Limit‐EventLog
  • New‐EventLog
  • Remove‐Computer
  • Remove‐EventLog
  • Set‐Service
  • Test‐EventLog
  • Show‐EventLog

The ‐ComputerName parameter is still used in PowerShell 7, just not to indicate that the cmdlet does remote processing internally, and it does not leverage PowerShell remoting.

PowerShell 7 does not implement the full Windows DSC feature. PowerShell 7 does implement Invoke‐DSCResource to invoke a DSC resource, but there is no local configuration manager, no push servers, or any of the other great features you used with DSC in Windows PowerShell.

The WSUS feature includes a module that is not compatible natively with PowerShell 7 and does not function acceptably in a compatibility session. The design of the UpdateServices module makes use of object methods, instead of the more usual approach of using cmdlets. Since methods are removed via serialization, you cannot use this module within the compatibility solution. If you want to manage WSUS, you must use Windows PowerShell.

Also, it is unlikely that there is an easy fix for the overall architecture of this module, since it relies on Simple Object Access Protocol (SOAP) for communications with the WSUS server. The .NET Core team has not implemented SOAP and appears to have no plans to do so. The long‐term solution is for the WSUS team to redesign their client‐server implementations to use Representational State Transfer (REST) or other supported protocols. At the time of writing, no plans have been announced.

The Best Practices module also does not work either natively or via the compatibility solution. The Best Practices team needs to redevelop the module, and feature, to make use of .NET Core.

The WebAdministration module, part of the IIS Management tools, includes a PowerShell provider. When you load this module in Windows PowerShell, Windows PowerShell loads the provider and creates an IIS: drive you use when you administer IIS. While the commands in both the IISAdministration and WebAdministration modules more or less work, any command that uses the provider would fail.

The Microsoft.PowerShell.Management module in PowerShell does not contain the Add‐Computer, Checkpoint‐Computer, Remove‐Computer, or Restore‐Computer cmdlets. You can use these commands by creating a Windows PowerShell remote session and invoking the commands in that remoting session.

Compatibility Issue Work‐Arounds

The default work‐around for any Windows PowerShell compatibility issue is simply to not use PowerShell 7 until there is a solution to your specific issues. Microsoft offers full support for Windows PowerShell 5.1 for the foreseeable future, so there is little risk in continuing to use Windows PowerShell.

What might be more effective is a hybrid strategy that combines running PowerShell 7 natively where you can or via the compatibility solution. Then use Windows PowerShell as needed until you can migrate fully to PowerShell 7.

For the few Windows PowerShell features not in PowerShell 7, there is no easy work‐around aside from continuing to use Windows PowerShell.

That is made more complex by the cross‐platform nature of PowerShell. It is harder to add features to PowerShell where .NET and the OS itself do not provide the necessary supporting features.

The modules and providers that do not work natively in PowerShell or via the compatibility list also have no easy solution. The code that would need to be updated to support PowerShell 7 natively is proprietary. The Microsoft product teams currently would need to make those changes, and for some teams, particularly the WSUS team, that could be a lot of otherwise unplanned work.

Some Microsoft product teams, for example the Active Directory team, were able to ensure the Active Directory module works with PowerShell 7, although you do need the latest version of that module. As Chapter 3, ”Managing Active Directory,” demonstrates, this means you can manage your AD database within PowerShell 7. But at the same time, the AD Deployment module, which you need to deploy AD in your environment, works only via the compatibility mechanism. So, you can install new forests, new domains, and new domain controllers via the compatibility solution.

For commands that formerly used the ‐ComputerName parameter, you can use Invoke‐Command to run the command remotely and return the results. This book makes extensive use of this feature to run commands on different computers.

Summary

In summary, PowerShell 7 has done a good job implementing backward compatibility with Windows PowerShell. Almost all of the features of Windows PowerShell are available to you in PowerShell.7. The compatibility solution, which uses implicit remoting, extends the set of commands available to you, although there are some minor issues in some cases. Finally, there are a few things that do not work in PowerShell 7, and for those you have other options.