| RFC | 0049 |
|---|---|
| Author | Steve Lee |
| Status | Draft |
| SupercededBy | n/a |
| Version | 1.0 |
| Area | Engine |
| Comments Due | 11/30 |
| Plan to implement | true |
When starting PowerShell from a different version of PowerShell, the PSModulePath should reflect the module search path and only include segments appropriate for the version of PowerShell being started.
As a PowerShell User,
I can start a different version of PowerShell within PowerShell,
so that I can do things required by that specific version of PowerShell.
PS7> $env:PSModulePath.split(';')
C:\Users\user\Documents\PowerShell\Modules
C:\Program Files\PowerShell\Modules
c:\program files\powershell\7-preview\Modules
C:\Program Files\WindowsPowerShell\Modules
C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules
PS7> powershell -noprofile
WinPS> $env:PSModulePath.split(';')
C:\Users\user\Documents\WindowsPowerShell\Modules
C:\Program Files\WindowsPowerShell\Modules
C:\WINDOWS\system32\WindowsPowerShell\v1.0\ModulesHere a custom C:\MyModules is added
PS7> $env:PSModulePath.split(';')
C:\Users\user\Documents\PowerShell\Modules
C:\Program Files\PowerShell\Modules
c:\program files\powershell\7-preview\Modules
C:\Program Files\WindowsPowerShell\Modules
C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules
C:\MyModules
PS7> powershell -noprofile
WinPS> $env:PSModulePath.split(';')
C:\Program Files\WindowsPowerShell\Modules
C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules
C:\MyModulesHere the System32 path is removed
PS7> $env:PSModulePath.split(';')
C:\Users\user\Documents\PowerShell\Modules
C:\Program Files\PowerShell\Modules
c:\program files\powershell\7-preview\Modules
C:\Program Files\WindowsPowerShell\Modules
PS7> powershell -noprofile
WinPS> $env:PSModulePath.split(';')
C:\Program Files\WindowsPowerShell\ModulesHere the System32 path is removed and the C:\MyModules path is added
PS7> $env:PSModulePath.split(';')
C:\Users\user\Documents\PowerShell\Modules
C:\Program Files\PowerShell\Modules
c:\program files\powershell\7-preview\Modules
C:\Program Files\WindowsPowerShell\Modules
C:\MyModules
PS7> powershell -noprofile
WinPS> $env:PSModulePath.split(';')
C:\Program Files\WindowsPowerShell\Modules
C:\MyModulesBy default, PowerShell starts with (in order):
| Segment Description | Windows PowerShell example | PowerShell 7 example |
|---|---|---|
| User modules | C:\Users\user\Documents\WindowsPowerShell\Modules | C:\Users\user\Documents\PowerShell\Modules |
| System modules | C:\Program Files\WindowsPowerShell\Modules | C:\Program Files\PowerShell\Modules |
| $PSHOME modules | C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules | c:\program files\powershell\7-preview\Modules |
| Windows modules | C:\WINDOWS\system32\WindowsPowerShell\v1.0\Module | |
| App added modules | C:\Program Files (x86)\Microsoft Azure Information Protection\Powershell |
Currently, applications (or user) adding additional paths to $env:PSModulePath are dropped in PowerShell 7.
Windows PowerShell has logic at startup to construct the PSModulePath based on (simplified version):
- If PSModulePath doesn't exist: Combine
Usermodules path,Systemmodules path, and$PSHOMEmodules path - If PSModulePath does exist:
- If PSModulePath contains
$PSHOMEmodules path:Systemmodules path is inserted before$PSHOMEmodules path if it's not there
- else:
- Just use
$env:PSModulePathas-is as user had deliberately removed$PSHOME
- Just use
- If PSModulePath contains
User module path is prefixed only if User scope $env:PSModulePath doesn't exist.
Otherwise, it is assumed the user removed it if it's not there already.
There are no changes planned for Windows PowerShell 5.1, so this existing logic is in place.
Currently, PSCore6 doesn't use contents of $env:PSModulePath if it detects it was started from PowerShell
and simply overwrites it with User modules path + System modules path + $PSHOME modules path + Windows modules path.
Intent is to have PSModulePath behave how Path env var works on Windows.
Path on Windows is treated differently from other env vars.
When a process is started, Windows will combine the User Path env var with the System Path env var.
However, for other env vars if the User env var exists, a new process will have that value only even if a Machine env var
with the same name exists.
In this case, the User version of the env var is preferred.
In the changes detailed below, PSModulePath adopts the Path behavior to have a combined value from User and System versions of that env var.
Change would be on Windows:
- Retrieve user
PSModulePathenv var from registry - Compare to process inherited
PSModulePathenv var- If the same:
- Append the
SystemPSModulePathto the end following the semantics of thePathenv var - The Windows system32 path comes from the machine defined
PSModulePathso does not need to be added explicitly
- Append the
- If different, treat as though user explicitly modified it and don't append
SystemPSModulePath
- If the same:
- Prefix with PS7 user, system, and $PSHOME paths in that order
- If
powershell.config.jsoncontains a user scopedPSModulePath, use that instead of the default for the user - If
powershell.config.jsoncontains a system scopedPSModulePath, use that instead of the default for the system
- If
Unix systems don't have a separation of User and System env vars so PSModulePath is inherited and PS7 specific paths are prefixed if not
already existing.
Note that Windows PowerShell here means both powershell.exe and powershell_ise.exe.
Copy process $env:PSModulePath as WinPSModulePath:
- Remove PS7
Usermodule path - Remove PS7
Systemmodule path - Remove PS7
$PSHOMEmodule path
Use that WinPSModulePath when starting Windows PowerShell.
The PowerShell 7 startup continues as-is with the addition of inheriting paths Windows PowerShell have added. Since the PS7 specific paths are prefixed, there is no functional issue.
PowerShell Core 6 clobbers $env:PSModulePath and no changes will be made.
The PowerShell 7 startup continues as-is with the addition of inheriting paths PowerShell Core 6 have added. Since the PS7 specific paths are prefixed, there is no functional issue.
There was a proposal to cache the inherited $env:PSModulePath on startup and pass it to Windows PowerShell when started.
However, this would not reflect any changes the user made and expect to be inherited by the child process.