Prior to Microsoft's release of the Windows Management Framework (WMF) version 5, we had no formal PowerShell support for multiple versions of the same PowerShell module.
Latest posts by Timothy Warner (see all)

Consider the problem: Your PSModulePath automatic variable contains a semicolon-separated list of directory paths that PowerShell uses to locate modules.

((Get-ChildItem -Path Env:\PSModulePath).value).split(";")
C:\Program Files\WindowsPowerShell\Modules

If you're developing a PowerShell module and your shop uses versioning, it's a pain in the neck to separate the modules in PowerShell v4 and prior. You normally have to perform any combination of the following workarounds:

  • Manually create subdirectories in your \Modules\ModuleName folder for each module version
  • Create/edit module manifests and specify a value for the ModuleVersion key
  • Add new directory paths to your PSModulePath variable

For example, here's a directory tree showing the layout for a two-version module named MyModule:

             MyModule.psd1 (manifest: ModuleVersion="1.0")
             MyModule.psd1 (manifest: ModuleVersion="1.1")

Honestly, this multiple module version situation wasn't a big deal for most of us,; that is to say, those of us who simply use modules rather than develop them. If a module author updated his or her module, we'd simply overwrite the previous version.

However, the advent of PowerShellGet has changed this landscape for us PowerShell administrators significantly. You're familiar with the PowerShellGet module manager, right? You can use native PowerShell or the PowerShell Gallery to browse for and install Microsoft- and community-authored modules.

Get-Command -Module PowerShellGet | Select-Object -Property Name

Working with multiple module versions

I'm on my Windows 10 Enterprise Edition computer, with the operating system all patched up and ready to rock. Here's my PowerShell version:

Major  Minor  Build  Revision
-----  -----  -----  --------
5      0      10240  16384

I'll use Dave Wyatt's wonderful Pester unit testing module for our example today. As you can see, Dave published several Pester versions to the PowerShell Gallery; here we'll examine only the last five:

Find-Module -Name Pester -AllVersions | Select-Object -Property Name, Version, PublishedDate | Sort-Object -Property PublishedDate -Descending | Select-Object -First 5
Name      Version    PublishedDate
----      -------    -------------
Pester    3.3.14     12/16/2015 2:01:30 PM
Pester    3.3.13     12/10/2015 3:10:06 PM
Pester    3.3.12     12/8/2015 11:12:50 PM
Pester    3.3.11     9/8/2015 3:17:29 PM
Pester    3.3.10     8/14/2015 6:37:24 PM

As logic will tell you, performing a standard module installation with PowerShellGet gives you the most recent version. Here, I'll test that now (I've snipped most of the output to show you what I think is most important):

Install-Module -Name pester -Verbose
VERBOSE: The specified module will be installed in 'C:\ProgramFiles\WindowsPowerShell\Modules'.
WARNING: Version '3.3.5' of module 'Pester' is already installed at 'C:\Program Files\WindowsPowerShell\Modules\Pester\3.3.5'. To delete version '3.3.5' and install version '3.3.14', run Install-Module, and add the -Force parameter.

We just learned three very important things:

  • As advertised, PowerShell v5 is indeed aware of multiple module versions
  • Windows 10 already ships with an older Pester version
  • We can use Install-Module -Force to nuke an existing module and replace it with a different version

Well, what if we want to keep Pester v3.3.5, and we want to install v3.3.14 as well? That's not a problem, thanks to the -RequiredVersion parameter of Install-Module:

Install-Module -Name pester -RequiredVersion 3.3.14 -Verbose
VERBOSE: The specified module will be installed in 'C:\ProgramFiles\WindowsPowerShell\Modules'.
VERBOSE: NuGet: Successfully installed 'Pester 3.3.14'.
VERBOSE: Module 'Pester' was installed successfully.

As you can see in the following screenshot, both Pester versions comfortably exist side-by-side on the same computer with no additional administrator intervention:

Side-by-side module versions in PowerShell v5

Side-by-side module versions in PowerShell v5

Invoking specific PowerShell module versions in your code

We now have two different Pester module versions installed on our Windows 10 box. Which version do you think is loaded into your PowerShell runspace by default? Let's check:

Get-Command -Module pester | Select-Object -Property name, version -First 3
Name                 Version
----                 -------
AfterAll             3.3.14
AfterEach            3.3.14
Assert-MockCalled    3.3.14

Not unexpectedly, we have access to the most recent module version by default. If we need to use the old (3.3.5) version, we'll first dump the 3.3.14 module out of our runspace:

Remove-Module -Name pester

And then we'll use the -RequiredVersion parameter of Import-Module:

Import-Module -Name pester -RequiredVersion 3.3.5

Let's verify:

Get-Command -Module pester | Select-Object -Property Name, Version -First 3
Name                Version
----                -------
AfterAll            3.3.5
AfterEach           3.3.5
Assert-MockCalled   3.3.5


To wrap up, I want to answer the question that some might have: namely, "Why in the world should I care about this?" Here's the deal: You may rely upon a particular module to do your work, and you discover only after installing a more recent version that it's no longer behaving as expected.

Subscribe to 4sysops newsletter!

Because we have multi-version support, we no longer have to worry about losing a "known good" module version. And if you're a module author, you can take comfort in the fact that users are less likely to have their processes break due to a buggy module release.

  1. Avatar
    Sam 5 years ago

    Nice article. How can I publish multiple versions of my module? I follow the same folder structure shown here but it is not working. I’m on WMF 5.1


  2. Avatar
    Devon 9 months ago

    Thanks for this!
    I am running into this with Posh-SSH. Version 2.2 will connect to older Cisco devices, whereas new versions won’t. And 2.2 won’t connect to other devices we have to access in the same script, but the new version will.
    Hopefully the info here will help us avoid finding and re-writing for a different SSH module/solution (sure looks like it will). 🙂

Leave a reply

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


© 4sysops 2006 - 2023


Please ask IT administration questions in the forums. Any other messages are welcome.


Log in with your credentials


Forgot your details?

Create Account