|
| 1 | +--- |
| 2 | +RFC: 0049 |
| 3 | +Author: Steve Lee |
| 4 | +Status: Draft |
| 5 | +SupercededBy: n/a |
| 6 | +Version: 1.0 |
| 7 | +Area: Engine |
| 8 | +Comments Due: 11/30 |
| 9 | +Plan to implement: Yes |
| 10 | +--- |
| 11 | + |
| 12 | +# PSModulePath When Starting PowerShell within PowerShell |
| 13 | + |
| 14 | +When starting PowerShell from a different version of PowerShell, the PSModulePath |
| 15 | +should reflect the module search path and only include segments appropriate for |
| 16 | +the version of PowerShell being started. |
| 17 | + |
| 18 | +## Motivation |
| 19 | + |
| 20 | + As a PowerShell User, |
| 21 | + I can start a different version of PowerShell within PowerShell, |
| 22 | + so that I can do things required by that specific version of PowerShell. |
| 23 | + |
| 24 | +## User Experience |
| 25 | + |
| 26 | +### Starting Windows PowerShell from PowerShell 7 |
| 27 | + |
| 28 | +```powershell |
| 29 | +PS7> $env:PSModulePath.split(';') |
| 30 | +C:\Users\user\Documents\PowerShell\Modules |
| 31 | +C:\Program Files\PowerShell\Modules |
| 32 | +c:\program files\powershell\7-preview\Modules |
| 33 | +C:\Program Files\WindowsPowerShell\Modules |
| 34 | +C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules |
| 35 | +
|
| 36 | +PS7> powershell -noprofile |
| 37 | +WinPS> $env:PSModulePath.split(';') |
| 38 | +C:\Users\user\Documents\WindowsPowerShell\Modules |
| 39 | +C:\Program Files\WindowsPowerShell\Modules |
| 40 | +C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules |
| 41 | +``` |
| 42 | + |
| 43 | +### Starting Windows PowerShell from PowerShell 7 with additions by user |
| 44 | + |
| 45 | +Here a custom `C:\MyModules` is added |
| 46 | + |
| 47 | +```powershell |
| 48 | +PS7> $env:PSModulePath.split(';') |
| 49 | +C:\Users\user\Documents\PowerShell\Modules |
| 50 | +C:\Program Files\PowerShell\Modules |
| 51 | +c:\program files\powershell\7-preview\Modules |
| 52 | +C:\Program Files\WindowsPowerShell\Modules |
| 53 | +C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules |
| 54 | +C:\MyModules |
| 55 | +
|
| 56 | +PS7> powershell -noprofile |
| 57 | +WinPS> $env:PSModulePath.split(';') |
| 58 | +C:\Program Files\WindowsPowerShell\Modules |
| 59 | +C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules |
| 60 | +C:\MyModules |
| 61 | +``` |
| 62 | + |
| 63 | +### Starting Windows PowerShell from PowerShell 7 with deletions by user |
| 64 | + |
| 65 | +Here the `System32` path is removed |
| 66 | + |
| 67 | +```powershell |
| 68 | +PS7> $env:PSModulePath.split(';') |
| 69 | +C:\Users\user\Documents\PowerShell\Modules |
| 70 | +C:\Program Files\PowerShell\Modules |
| 71 | +c:\program files\powershell\7-preview\Modules |
| 72 | +C:\Program Files\WindowsPowerShell\Modules |
| 73 | +
|
| 74 | +PS7> powershell -noprofile |
| 75 | +WinPS> $env:PSModulePath.split(';') |
| 76 | +C:\Program Files\WindowsPowerShell\Modules |
| 77 | +``` |
| 78 | + |
| 79 | +### Starting Windows PowerShell from PowerShell 7 with additions and deletions by user |
| 80 | + |
| 81 | +Here the `System32` path is removed and the `C:\MyModules` path is added |
| 82 | + |
| 83 | +```powershell |
| 84 | +PS7> $env:PSModulePath.split(';') |
| 85 | +C:\Users\user\Documents\PowerShell\Modules |
| 86 | +C:\Program Files\PowerShell\Modules |
| 87 | +c:\program files\powershell\7-preview\Modules |
| 88 | +C:\Program Files\WindowsPowerShell\Modules |
| 89 | +C:\MyModules |
| 90 | +
|
| 91 | +PS7> powershell -noprofile |
| 92 | +WinPS> $env:PSModulePath.split(';') |
| 93 | +C:\Program Files\WindowsPowerShell\Modules |
| 94 | +C:\MyModules |
| 95 | +``` |
| 96 | + |
| 97 | +## Specification |
| 98 | + |
| 99 | +By default, PowerShell starts with (in order): |
| 100 | + |
| 101 | +| Segment Description | Windows PowerShell example | PowerShell 7 example | |
| 102 | +|---------------------|--------------------------------------------------------------------------|---------------------------------------------------| |
| 103 | +| User modules | C:\Users\user\Documents\WindowsPowerShell\Modules | C:\Users\user\Documents\PowerShell\Modules | |
| 104 | +| System modules | C:\Program Files\WindowsPowerShell\Modules | C:\Program Files\PowerShell\Modules | |
| 105 | +| $PSHOME modules | C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules | c:\program files\powershell\7-preview\Modules | |
| 106 | +| Windows modules | | C:\WINDOWS\system32\WindowsPowerShell\v1.0\Module | |
| 107 | +| App added modules | C:\Program Files (x86)\Microsoft Azure Information Protection\Powershell | | |
| 108 | + |
| 109 | +Currently, applications (or user) adding additional paths to `$env:PSModulePath` are dropped in PowerShell 7. |
| 110 | + |
| 111 | +### Windows PowerShell PSModulePath construction |
| 112 | + |
| 113 | +Windows PowerShell has logic at startup to construct the PSModulePath based on (simplified version): |
| 114 | + |
| 115 | +- If PSModulePath doesn't exist: Combine `User` modules path, `System` modules path, and `$PSHOME` modules path |
| 116 | +- If PSModulePath does exist: |
| 117 | + - If PSModulePath contains `$PSHOME` modules path: |
| 118 | + - `System` modules path is inserted before `$PSHOME` modules path if it's not there |
| 119 | + - else: |
| 120 | + - Just use `$env:PSModulePath` as-is as user had deliberately removed `$PSHOME` |
| 121 | + |
| 122 | +`User` module path is prefixed only if User scope `$env:PSModulePath` doesn't exist. |
| 123 | +Otherwise, it is assumed the user removed it if it's not there already. |
| 124 | + |
| 125 | +There are no changes planned for Windows PowerShell 5.1, so this existing logic is in place. |
| 126 | + |
| 127 | +### PowerShell 7 startup |
| 128 | + |
| 129 | +Currently, PSCore6 doesn't use contents of `$env:PSModulePath` if it detects it was started from PowerShell |
| 130 | +and simply overwrites it with `User` modules path + `System` modules path + `$PSHOME` modules path + `Windows` modules path. |
| 131 | + |
| 132 | +Intent is to have `PSModulePath` behave how `Path` env var works on Windows. |
| 133 | +`Path` on Windows is treated differently from other env vars. |
| 134 | +When a process is started, Windows will combine the `User` `Path` env var with the `System` `Path` env var. |
| 135 | +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 |
| 136 | +with the same name exists. |
| 137 | +In this case, the `User` version of the env var is preferred. |
| 138 | +In the changes detailed below, `PSModulePath` adopts the `Path` behavior to have a combined value from `User` and `System` versions of that env var. |
| 139 | + |
| 140 | +Change would be on Windows: |
| 141 | + |
| 142 | +- Retrieve user `PSModulePath` env var from registry |
| 143 | +- Compare to process inherited `PSModulePath` env var |
| 144 | + - If the same: |
| 145 | + - Append the `System` `PSModulePath` to the end following the semantics of the `Path` env var |
| 146 | + - The Windows system32 path comes from the machine defined `PSModulePath` so does not need to be added explicitly |
| 147 | + - If different, treat as though user explicitly modified it and don't append `System` `PSModulePath` |
| 148 | +- Prefix with PS7 user, system, and $PSHOME paths in that order |
| 149 | + - If `powershell.config.json` contains a user scoped `PSModulePath`, use that instead of the default for the user |
| 150 | + - If `powershell.config.json` contains a system scoped `PSModulePath`, use that instead of the default for the system |
| 151 | + |
| 152 | +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 |
| 153 | +already existing. |
| 154 | + |
| 155 | +### Starting Windows PowerShell from PowerShell 7 Implementation |
| 156 | + |
| 157 | +Note that Windows PowerShell here means both `powershell.exe` and `powershell_ise.exe`. |
| 158 | + |
| 159 | +Copy process `$env:PSModulePath` as `WinPSModulePath`: |
| 160 | + |
| 161 | +- Remove PS7 `User` module path |
| 162 | +- Remove PS7 `System` module path |
| 163 | +- Remove PS7 `$PSHOME` module path |
| 164 | + |
| 165 | +Use that `WinPSModulePath` when starting Windows PowerShell. |
| 166 | + |
| 167 | +### Starting PowerShell 7 from Windows PowerShell |
| 168 | + |
| 169 | +The PowerShell 7 startup continues as-is with the addition of inheriting paths Windows PowerShell have added. |
| 170 | +Since the PS7 specific paths are prefixed, there is no functional issue. |
| 171 | + |
| 172 | +### Starting PowerShell 6 from PowerShell 7 |
| 173 | + |
| 174 | +PowerShell Core 6 clobbers `$env:PSModulePath` and no changes will be made. |
| 175 | + |
| 176 | +### Starting PowerShell 7 from PowerShell 6 |
| 177 | + |
| 178 | +The PowerShell 7 startup continues as-is with the addition of inheriting paths PowerShell Core 6 have added. |
| 179 | +Since the PS7 specific paths are prefixed, there is no functional issue. |
| 180 | + |
| 181 | +## Alternate Proposals and Considerations |
| 182 | + |
| 183 | +There was a proposal to cache the inherited `$env:PSModulePath` on startup and pass it to Windows PowerShell when started. |
| 184 | +However, this would not reflect any changes the user made and expect to be inherited by the child process. |
0 commit comments