From c60b761864485d67e2d530fb0a318ea5f89d26ea Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Mon, 11 Nov 2019 17:15:02 -0800 Subject: [PATCH 1/6] Draft of PSModulePath behavior in PS7 --- 1-Draft/RFCxxxx-PSModulePath.md | 168 ++++++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 1-Draft/RFCxxxx-PSModulePath.md diff --git a/1-Draft/RFCxxxx-PSModulePath.md b/1-Draft/RFCxxxx-PSModulePath.md new file mode 100644 index 00000000..0eb89b41 --- /dev/null +++ b/1-Draft/RFCxxxx-PSModulePath.md @@ -0,0 +1,168 @@ +--- +RFC: +Author: Steve Lee +Status: Draft +SupercededBy: n/a +Version: 1.0 +Area: Engine +Comments Due: 11/30 +Plan to implement: Yes +--- + +# PSModulePath When Starting PowerShell within PowerShell + +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. + +## Motivation + + 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. + +## User Experience + +### Starting Windows PowerShell from PowerShell 7 + + ```powershell + PS7> $env:PSModulePath.split(';') + C:\Users\slee\Documents\PowerShell\Modules + C:\Program Files\PowerShell\Modules + c:\program files\powershell\7-preview\Modules + C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules + + PS7> powershell -noprofile + WinPS> $env:PSModulePath.split(';') + C:\Users\slee\Documents\WindowsPowerShell\Modules + C:\Program Files\WindowsPowerShell\Modules + C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules + C:\Program Files (x86)\Microsoft Azure Information Protection\Powershell + ``` + +### Starting Windows PowerShell from PowerShell 7 with additions by user + + ```powershell + PS7> $env:PSModulePath.split(';') + C:\Users\slee\Documents\PowerShell\Modules + C:\Program Files\PowerShell\Modules + c:\program files\powershell\7-preview\Modules + C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules + C:\MyModules + + PS7> powershell -noprofile + WinPS> $env:PSModulePath.split(';') + C:\Users\slee\Documents\WindowsPowerShell\Modules + C:\Program Files\WindowsPowerShell\Modules + C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules + C:\Program Files (x86)\Microsoft Azure Information Protection\Powershell + C:\MyModules + ``` + +### Starting Windows PowerShell from PowerShell 7 with deletions by user + + ```powershell + PS7> $env:PSModulePath.split(';') + C:\Users\slee\Documents\PowerShell\Modules + C:\Program Files\PowerShell\Modules + c:\program files\powershell\7-preview\Modules + + PS7> powershell -noprofile + WinPS> $env:PSModulePath.split(';') + C:\Users\slee\Documents\WindowsPowerShell\Modules + C:\Program Files\WindowsPowerShell\Modules + C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules + C:\Program Files (x86)\Microsoft Azure Information Protection\Powershell + ``` + +### Starting Windows PowerShell from PowerShell 7 with additions and deletions by user + + ```powershell + PS7> $env:PSModulePath.split(';') + C:\Users\slee\Documents\PowerShell\Modules + C:\Program Files\PowerShell\Modules + c:\program files\powershell\7-preview\Modules + C:\MyModules + + PS7> powershell -noprofile + WinPS> $env:PSModulePath.split(';') + C:\Users\slee\Documents\WindowsPowerShell\Modules + C:\Program Files\WindowsPowerShell\Modules + C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules + C:\Program Files (x86)\Microsoft Azure Information Protection\Powershell + C:\MyModules + ``` + +## Specification + +By default, PowerShell starts with (in order): + +| Segment Description | Windows PowerShell example | PowerShell 7 example | +|---------------------|--------------------------------------------------------------------------|---------------------------------------------------| +| User modules | C:\Users\slee\Documents\WindowsPowerShell\Modules | C:\Users\slee\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 PSModulePath construction + +Windows PowerShell has logic at startup to construct the PSModulePath based on (simplified version): + +- If PSModulePath doesn't exist: Combine `User` modules path, `System` modules path, and `$PSHOME` modules path +- If PSModulePath does exist: + - If PSModulePath contains `$PSHOME` modules path: + - `System` modules path is inserted before `$PSHOME` modules path if it's not there + - else: + - Just use `$env:PSModulePath` as-is as user had deliberately removed `$PSHOME` + +`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. + +### PowerShell 7 startup + +Currently, PS7 doesn't use contents of `$env:PSModulePath` and simply overwrites it with `User` modules path + +`System` modules path + `$PSHOME` modules path + `Windows` modules path. + +Change would be to use `$env:PSModulePath`, but prefix with `User` modules path + `System` modules path + +`$PSHOME` modules path if any of those paths are not already there. + +`Windows` modules path will already be there along with any additional paths added by the user or applications. + +### Starting Windows PowerShell from PowerShell 7 Implementation + +Note that Windows PowerShell here means both `powershell.exe` and `powershell_ise.exe`. + +Copy process `$env:PSModulePath` as `PS7PSModulePath`: + +- Remove PS7 `User` module path +- Remove PS7 `System` module path +- Remove PS7 `$PSHOME` module path + +Get fresh `$env:PSModulePath` from `Machine` scope if exists, otherwise `User` scope +as `WinPSModulePath`. + +If any of the Windows PowerShell path segments was removed in `PS7PSModulePath`, then +remove it from `WinPSModulePath`. + +Any segments in `WinPSModulePath` that exists in `PS7PSModulePath` are removed from +`PS7PSModulePath`. + +Append remainder of `PS7PSModulePath` to end of `WinPSModulePath`. +Use that `WinPSModulePath` when starting Windows PowerShell. + +### Starting PowerShell 7 from Windows PowerShell + +The PowerShell 7 startup covers this case and should just work. + +### Starting PowerShell 6 from PowerShell 7 + +PowerShell Core 6 clobbers `$env:PSModulePath` and no changes will be made. + +### Starting PowerShell 7 from PowerShell 6 + +The PowerShell 7 startup covers this case and should just work. + +## Alternate Proposals and Considerations From 9306f44d4fe5c5edd0cb581afa33c044d91d673d Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Tue, 12 Nov 2019 09:20:47 -0800 Subject: [PATCH 2/6] clarify PS7 current behavior --- 1-Draft/RFCxxxx-PSModulePath.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/1-Draft/RFCxxxx-PSModulePath.md b/1-Draft/RFCxxxx-PSModulePath.md index 0eb89b41..6e3dada3 100644 --- a/1-Draft/RFCxxxx-PSModulePath.md +++ b/1-Draft/RFCxxxx-PSModulePath.md @@ -123,8 +123,8 @@ Otherwise, it is assumed the user removed it if it's not there already. ### PowerShell 7 startup -Currently, PS7 doesn't use contents of `$env:PSModulePath` and simply overwrites it with `User` modules path + -`System` modules path + `$PSHOME` modules path + `Windows` modules path. +Currently, PS7 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. Change would be to use `$env:PSModulePath`, but prefix with `User` modules path + `System` modules path + `$PSHOME` modules path if any of those paths are not already there. From 62a15fd0c76401daf98c774b2da6ff948891c0ef Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Tue, 12 Nov 2019 12:41:57 -0800 Subject: [PATCH 3/6] remove unncessary steps in constructing WinPS PSModulePath --- 1-Draft/RFCxxxx-PSModulePath.md | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/1-Draft/RFCxxxx-PSModulePath.md b/1-Draft/RFCxxxx-PSModulePath.md index 6e3dada3..2fe0dd09 100644 --- a/1-Draft/RFCxxxx-PSModulePath.md +++ b/1-Draft/RFCxxxx-PSModulePath.md @@ -135,22 +135,12 @@ Change would be to use `$env:PSModulePath`, but prefix with `User` modules path Note that Windows PowerShell here means both `powershell.exe` and `powershell_ise.exe`. -Copy process `$env:PSModulePath` as `PS7PSModulePath`: +Copy process `$env:PSModulePath` as `WinPSModulePath`: - Remove PS7 `User` module path - Remove PS7 `System` module path - Remove PS7 `$PSHOME` module path -Get fresh `$env:PSModulePath` from `Machine` scope if exists, otherwise `User` scope -as `WinPSModulePath`. - -If any of the Windows PowerShell path segments was removed in `PS7PSModulePath`, then -remove it from `WinPSModulePath`. - -Any segments in `WinPSModulePath` that exists in `PS7PSModulePath` are removed from -`PS7PSModulePath`. - -Append remainder of `PS7PSModulePath` to end of `WinPSModulePath`. Use that `WinPSModulePath` when starting Windows PowerShell. ### Starting PowerShell 7 from Windows PowerShell From be58acf50c3e47bc0aed011062ee3360db729070 Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Tue, 12 Nov 2019 13:21:30 -0800 Subject: [PATCH 4/6] clarify `Windows` module path on PS7 startup --- 1-Draft/RFCxxxx-PSModulePath.md | 1 + 1 file changed, 1 insertion(+) diff --git a/1-Draft/RFCxxxx-PSModulePath.md b/1-Draft/RFCxxxx-PSModulePath.md index 2fe0dd09..e70658bd 100644 --- a/1-Draft/RFCxxxx-PSModulePath.md +++ b/1-Draft/RFCxxxx-PSModulePath.md @@ -130,6 +130,7 @@ Change would be to use `$env:PSModulePath`, but prefix with `User` modules path `$PSHOME` modules path if any of those paths are not already there. `Windows` modules path will already be there along with any additional paths added by the user or applications. +If the `Windows` modules path is not in `$env:PSModulePath`, then it will not be added as the user removed it. ### Starting Windows PowerShell from PowerShell 7 Implementation From 80635df1b855db662b6a3dd4ee2c1b1f40cbb21e Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Thu, 30 Jan 2020 12:40:04 -0800 Subject: [PATCH 5/6] Update adding more details. Fix examples to be consistent. --- 1-Draft/RFCxxxx-PSModulePath.md | 157 ++++++++++++++++++-------------- 1 file changed, 91 insertions(+), 66 deletions(-) diff --git a/1-Draft/RFCxxxx-PSModulePath.md b/1-Draft/RFCxxxx-PSModulePath.md index e70658bd..7e6455b6 100644 --- a/1-Draft/RFCxxxx-PSModulePath.md +++ b/1-Draft/RFCxxxx-PSModulePath.md @@ -25,73 +25,74 @@ the version of PowerShell being started. ### Starting Windows PowerShell from PowerShell 7 - ```powershell - PS7> $env:PSModulePath.split(';') - C:\Users\slee\Documents\PowerShell\Modules - C:\Program Files\PowerShell\Modules - c:\program files\powershell\7-preview\Modules - C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules - - PS7> powershell -noprofile - WinPS> $env:PSModulePath.split(';') - C:\Users\slee\Documents\WindowsPowerShell\Modules - C:\Program Files\WindowsPowerShell\Modules - C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules - C:\Program Files (x86)\Microsoft Azure Information Protection\Powershell - ``` +```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\Modules +``` ### Starting Windows PowerShell from PowerShell 7 with additions by user - ```powershell - PS7> $env:PSModulePath.split(';') - C:\Users\slee\Documents\PowerShell\Modules - C:\Program Files\PowerShell\Modules - c:\program files\powershell\7-preview\Modules - C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules - C:\MyModules - - PS7> powershell -noprofile - WinPS> $env:PSModulePath.split(';') - C:\Users\slee\Documents\WindowsPowerShell\Modules - C:\Program Files\WindowsPowerShell\Modules - C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules - C:\Program Files (x86)\Microsoft Azure Information Protection\Powershell - C:\MyModules - ``` +Here a custom `C:\MyModules` is added + +```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 +C:\MyModules + +PS7> powershell -noprofile +WinPS> $env:PSModulePath.split(';') +C:\Program Files\WindowsPowerShell\Modules +C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules +C:\MyModules +``` ### Starting Windows PowerShell from PowerShell 7 with deletions by user - ```powershell - PS7> $env:PSModulePath.split(';') - C:\Users\slee\Documents\PowerShell\Modules - C:\Program Files\PowerShell\Modules - c:\program files\powershell\7-preview\Modules +Here the `System32` path is removed - PS7> powershell -noprofile - WinPS> $env:PSModulePath.split(';') - C:\Users\slee\Documents\WindowsPowerShell\Modules - C:\Program Files\WindowsPowerShell\Modules - C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules - C:\Program Files (x86)\Microsoft Azure Information Protection\Powershell - ``` +```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 + +PS7> powershell -noprofile +WinPS> $env:PSModulePath.split(';') +C:\Program Files\WindowsPowerShell\Modules +``` ### Starting Windows PowerShell from PowerShell 7 with additions and deletions by user - ```powershell - PS7> $env:PSModulePath.split(';') - C:\Users\slee\Documents\PowerShell\Modules - C:\Program Files\PowerShell\Modules - c:\program files\powershell\7-preview\Modules - C:\MyModules - - PS7> powershell -noprofile - WinPS> $env:PSModulePath.split(';') - C:\Users\slee\Documents\WindowsPowerShell\Modules - C:\Program Files\WindowsPowerShell\Modules - C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules - C:\Program Files (x86)\Microsoft Azure Information Protection\Powershell - C:\MyModules - ``` +Here the `System32` path is removed and the `C:\MyModules` path is added + +```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:\MyModules + +PS7> powershell -noprofile +WinPS> $env:PSModulePath.split(';') +C:\Program Files\WindowsPowerShell\Modules +C:\MyModules +``` ## Specification @@ -99,7 +100,7 @@ By default, PowerShell starts with (in order): | Segment Description | Windows PowerShell example | PowerShell 7 example | |---------------------|--------------------------------------------------------------------------|---------------------------------------------------| -| User modules | C:\Users\slee\Documents\WindowsPowerShell\Modules | C:\Users\slee\Documents\PowerShell\Modules | +| 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 | @@ -121,16 +122,35 @@ Windows PowerShell has logic at startup to construct the PSModulePath based on ( `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. + ### PowerShell 7 startup -Currently, PS7 doesn't use contents of `$env:PSModulePath` if it detects it was started from PowerShell +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. -Change would be to use `$env:PSModulePath`, but prefix with `User` modules path + `System` modules path + -`$PSHOME` modules path if any of those paths are not already there. - -`Windows` modules path will already be there along with any additional paths added by the user or applications. -If the `Windows` modules path is not in `$env:PSModulePath`, then it will not be added as the user removed it. +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 `PSModulePath` env var from registry +- Compare to process inherited `PSModulePath` env var + - If the same: + - Append the `System` `PSModulePath` to the end following the semantics of the `Path` env var + - The Windows system32 path comes from the machine defined `PSModulePath` so does not need to be added explicitly + - If different, treat as though user explicitly modified it and don't append `System` `PSModulePath` +- Prefix with PS7 user, system, and $PSHOME paths in that order + - If `powershell.config.json` contains a user scoped `PSModulePath`, use that instead of the default for the user + - If `powershell.config.json` contains a system scoped `PSModulePath`, use that instead of the default for the system + +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. ### Starting Windows PowerShell from PowerShell 7 Implementation @@ -146,7 +166,8 @@ Use that `WinPSModulePath` when starting Windows PowerShell. ### Starting PowerShell 7 from Windows PowerShell -The PowerShell 7 startup covers this case and should just work. +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. ### Starting PowerShell 6 from PowerShell 7 @@ -154,6 +175,10 @@ PowerShell Core 6 clobbers `$env:PSModulePath` and no changes will be made. ### Starting PowerShell 7 from PowerShell 6 -The PowerShell 7 startup covers this case and should just work. +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. ## Alternate Proposals and Considerations + +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. From 009c4a9fc82fe33eeb60cb2f255b1134dea5d73c Mon Sep 17 00:00:00 2001 From: Joey Aiello Date: Wed, 5 Feb 2020 15:16:30 -0800 Subject: [PATCH 6/6] Prep RFC0049 (PSModulePath with WinPS/PSCore) for merge --- .../RFCxxxx-PSModulePath.md => 5-Final/RFC0049-PSModulePath.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename 1-Draft/RFCxxxx-PSModulePath.md => 5-Final/RFC0049-PSModulePath.md (99%) diff --git a/1-Draft/RFCxxxx-PSModulePath.md b/5-Final/RFC0049-PSModulePath.md similarity index 99% rename from 1-Draft/RFCxxxx-PSModulePath.md rename to 5-Final/RFC0049-PSModulePath.md index 7e6455b6..4fe85dc0 100644 --- a/1-Draft/RFCxxxx-PSModulePath.md +++ b/5-Final/RFC0049-PSModulePath.md @@ -1,5 +1,5 @@ --- -RFC: +RFC: 0049 Author: Steve Lee Status: Draft SupercededBy: n/a