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:\Users\Tim\Documents\WindowsPowerShell\Modules C:\Program Files\WindowsPowerShell\Modules C:\Windows\system32\WindowsPowerShell\v1.0\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:
C:\Users\Tim\Documents\WindowsPowerShell\Modules MyModulev1 MyModule MyModule.psd1 (manifest: ModuleVersion="1.0") MyModulev1.1 MyModule 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 Name ---- Find-Module Get-InstalledModule Get-PSRepository Install-Module Publish-Module Register-PSRepository Save-Module Set-PSRepository Uninstall-Module Unregister-PSRepository Update-Module
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:
$PSVersionTable.PSVersion 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:
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
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.