As a general rule, most people can just edit the PSModulePath variable in their Environment Variables ...
However, I am not most people.
I want to test all the things, and I want to use PowerShell 7, 6, and 5.1 interchangeably:
- I have to use PowerShell 5.1 for most of our work code (we're provisioning Windows 10 / Server 2016 / Server 2019 ... and 5.1 is what's on the box)
- I want to use PowerShell 7.x (whatever the current release is)
- I want to test the current "pre-release" version
Additionally, to make matters more confusing ... I sometimes work on Windows and sometimes on Linux (most notably in Azure's CloudShell). I've started mapping my CloudShell profile folder to a drive letter (A: for Azure) on my dev boxes, and loading my the profile Module from there. In those cases, I absolutely must alter the default PSModulePath to include that A: path
In any case, I don't ever want to install multiple copies of the Azure modules and keep them all up to date. One copy works interchangeably in all versions of PowerShell. It would be a waste of time to install that same version three times (once for each version of PowerShell), but that's what Microsoft seems to want me to do.
So here's what I do:
- I start with the current PSModulePath
- I use a Profile module which I put in that path
- I tweak the PSModulePath in a script in that module which I dot-source from the actual
$profile
script
For the rare case where there are different versions of modules for PowerShell 5.1 and PowerShell 7, I need to make sure the right one loads, so obviously I want to keep both "Modules" folders available, and have each version of PowerShell search its own version-specific path first, and then fall back to the other version's Modules folder. Because the order of paths must be different depending on which version of PowerShell is running, I can't just hardcode the PSModulePath environment variable.
Of course, I won't keep separate per-version profiles, so I calculate all of this in my profile script. Here's what I do:
- I prepend the location the script is running from (probably my cloud drive) and the location my profile.ps1 was in.
- I include the current PSModulePath (all the paths in the environment variable)
- I append all the other possible PSModulePaths!
I add all these paths into a big array, and then I get the right paths for them, and filter out duplicates and non-existent folders.
- The main concern is to keep things in order:
- User path ($Home) before machine path ($PSHome)
- Existing PSModulePath before other versions
- current version before other versions
- I don't worry about duplicates because
Select-UniquePath
takes care of it - I don't worry about missing paths, because
Select-UniquePath
takes care of it - I don't worry about Windows x86, because I never use that.
- I don't worry about linux, because I add paths based on
$PSScriptRoot
,$Profile
and$PSHome
Set-Variable ProfileDir (Split-Path $Profile.CurrentUserAllHosts -Parent) -Scope Global -Option AllScope, Constant -ErrorAction SilentlyContinue
$Env:PSModulePath =
# Prioritize "this" location (e.g. CloudDrive) UNLESS it's ~\projects\modules
@(if (($ModuleRootParent = Split-Path $PSScriptRoot) -ne "$Home\Projects\Modules") { $ModuleRootParent }) +
# The normal first location in PSModulePath is the "Modules" folder next to the real profile:
@(Join-Path $ProfileDir Modules) +
# After that, whatever is in the environment variable
@($Env:PSModulePath) +
# PSHome is where powershell.exe or pwsh.exe lives ... it should already be in the Env:PSModulePath, but just in case:
@(Join-Path $PSHome Modules) +
# FINALLY, add the Module paths for other PowerShell versions, because I'm an optimist
@(Join-Path (Split-Path (Split-Path $PSHome)) *PowerShell\ | Convert-Path | Get-ChildItem -Filter Modules -Directory -Recurse -Depth 2).FullName +
@(Convert-Path @(
Split-Path $ProfileDir | Join-Path -ChildPath *PowerShell\Modules
# These may be duplicate or not exist, but it doesn't matter
"$Env:ProgramFiles\*PowerShell\Modules"
"$Env:ProgramFiles\*PowerShell\*\Modules"
"$Env:SystemRoot\System32\*PowerShell\*\Modules"
)) +
# Guarantee my ~\Projects\Modules are there so I can load my dev projects
@("$Home\Projects\Modules") +
# To ensure canonical path case, wildcard every path separator and then convert-path
@() | Select-UniquePath