Search code examples
powershellpowershell-5.0

PSModulePath variable requirements before PowerShell starts


WPS = Windows PowerShell
PSC = PowerShell Core

When I started, I had three (3) paths as part of the System environment variable PSModulePath.

%ProgramFiles%\WindowsPowerShell\Modules
%SystemRoot%\system32\WindowsPowerShell\v1.0\Modules
C:\Program Files (x86)\Microsoft SQL Server\140\Tools\PowerShell\Modules\

I assume that the install of SQL Server created the last one. But, I removed the first two to see if the PowerShells could find their own way or if they needed them?

WPS 5.1 64-bit appears to start ok, but ISE reports an error finding `ISE`.
PSC 6.2 starts ok. There is no ISE.
PSC 7.0 starts ok. There is no ISE.

I had to put back the %SystemRoot%\system32\WindowsPowerShell\v1.0\Modules directory into the System environment variable PSModulePath. After that, WPS 5.1 and ISE start correctly.

Does WPS 5 -need- the System32 directory in the PSModulePath before it starts? I would like to remove it. And, actually, I would like to move the SQL Server directory to a more appropriate location. Is there a better place? I often run powershell -NoProfile from .bat file scripts, so I do not think any of the six (6) profile scripts (12 if 32-bit is added) is a good place. What do you suggest?

Update

=== start cmd.exe

C:>echo %PSModulePath%
C:\Program Files (x86)\Microsoft SQL Server\140\Tools\PowerShell\Modules\;

=== run powershell.exe 5.1.18362.145

C:>$Env:PSModulePath
C:\Users\lit\Documents\WindowsPowerShell\Modules;C:\Program Files (x86)\Microsoft SQL Server\140\Tools\PowerShell\Modules\;C:\Program Files\WindowsPowerShell\Modules

=== run ise

ipmo : The specified module 'ISE' was not loaded because no valid module file was found in any module directory.
At line:1 char:1
+ ipmo ISE
+ ~~~~~~~~
    + CategoryInfo          : ResourceUnavailable: (ISE:String) [Import-Module], FileNotFoundException
    + FullyQualifiedErrorId : Modules_ModuleNotFound,Microsoft.PowerShell.Commands.ImportModuleCommand

Solution

  • As an aside, re:

    PSC 6.2 [7.0] starts ok. There is no ISE.

    The ISE is a self-contained executable that hosts PowerShell, not the other way around. It can only host Windows PowerShell, not PowerShell Core, so you cannot use it to develop PowerShell Core code.

    Re your follow-up question, "Should I expect there to ever be an ISE for PSC, or should I think VS Code is the thing to use?"

    Indeed, Visual Studio Code with its PowerShell extension is the editor to use going forward: It is capable of targeting both Windows PowerShell and PowerShell Core, and all future development effort will go there.

    By contrast, the Windows PowerShell-only ISE will see no new features. (While .NET Core 3.0 hypothetically paves the way for porting the WPF-based ISE to .NET Core, I don't think that will happen.)


    WPS 5.1 64-bit appears to start ok, but ISE reports an error finding ISE.

    Omitting directory $env:SYSTEMROOT\system32\WindowsPowerShell\v1.0\Modules from $env:PSModulePath is ill-advised and results in the following symptoms:

    • Commands from the important system (shipped-with-Windows PowerShell) modules located there are still executable, because PowerShell apparently implicitly consults this location.

    • However, command discovery, tab completion, and explicit calls to Import-Module by module name (as opposed to full path) then stop working:

      • Case in point is the startup behavior of the ISE, which tries to explicitly load the ISE module (among others) with ipmo ISE (Import- Module), which fails.
      • That implicitly loading the module still works can be verified by simply directly invoking one of its commands, e.g., New-ISESnippet -?.

    When it comes to determining the effective $env:PSModulePath value, the PS editions differ fundamentally:

    Windows PowerShell (WPS) uses complicated rules to determine what directories to automatically add to $env:PSModulePath; in short:

    • The all-users module dir., $env:ProgramFiles%\WindowsPowerShell\Modules is always automatically added, if your $env:PSModulePath value is predefined in the registry (by default, it is predefined, and set to $env:SYSTEMROOT\system32\WindowsPowerShell\v1.0\Modules).
    • The current-user module dir. is only added if there's no user-level registry definition.

    • You can override all registry definitions with a process-level value of $env:PSModulePath set ad hoc before calling powershell.exe.

    PowerShell Core (WPC):

    • completely ignores any preexisting registry-based definitions of $env:PSModulePath and always defines its own list of standard dirs. on startup, comprising the PSC counterparts to the WPS locations plus WPS' system-module directory, $env:SYSTEMROOT\system32\WindowsPowerShell\v1.0\Modules (because an increasing number of WPS system modules are also usable from PSC).

      • This unexpected behavior was first reported in this GitHub issue, ...
      • ... which led to this RFC, which is still pending as of this writing, with the ultimate resolution not yet decided.
    • injects a process-level value of $env:PSModulePath set ad hoc before calling powershell.exe into its list of standard directories, after the current-user entry - assuming that value differs from any registry-based definition.[1]

      • If it weren't for the obscure distinction between a registry-based value and a - differing - ad hoc value, PSC's behavior is arguably more sensible: it only allows adding to the list of standard dirs. via a preexisting $env:PSModulePath environment-variable value, not replacing it.

      • Ignoring the registry-based definitions makes sense in that users may have used it to point to directories containing WSM-only modules. If a different variable name had been chosen for PSC, however, that problem could have been avoided - and that's indeed a solution being considered in the linked RFC.


    I would like to move the SQL Server directory to a more appropriate location. Is there a better place? I often run powershell -NoProfile from .bat file scripts

    Without modifying the registry-based definition of $env:PSModulePath:

    • The only directory that would work for both editions would be $env:SYSTEMROOT\system32\WindowsPowerShell\v1.0\Modules, but given that this directory is for module that ship with WPS, that's not appropriate.

    • Edition-specifically, you can choose:

      • WPS: $env:ProgramFiles\WindowsPowerShell\Modules
      • PSC: $env:ProgramFiles\PowerShell\Modules

    With modifying the registry-based definition of $env:PSModulePath:

    • For WPS, you can modify the machine-level registry definition of $env:PSModulePath at \HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment (which you can also modify via sysdm.cpl, Advanced > Environment Variables...): add your directory of choice to the existing value there.

    • Regrettably, as of this writing, that won't work for PSC, because it ignores registry-based definitions, as stated.


    [1] It seems that the test for difference is sloppily implemented, in that setting an ad hoc value for which a registry-based value is a prefix match is considered identical; e.g., if value c:\modules is defined in the registry, an ad hoc value of c:\modules.core makes PowerShell Core think that the ad hoc value does not differ and ignores it.
    Aside from that, this selective ignoring of environment-variable values depending on their method of definition is highly obscure.