Powershell: Modules under C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules not found with PowerShell 7-preview.6

Created on 23 Nov 2019  路  21Comments  路  Source: PowerShell/PowerShell

After updating to PowerShell 7-preview.6, I'm no longer able to import the ActiveDirectory module without providing the full path due to C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules no longer being in $env:PSModulePath. I know there was an update to Import-Module with this version that would use implicit remoting to import the module via Windows PowerShell, but it does not appear to be working as expected.

Steps to reproduce

Import-Module ActiveDirectory

Expected behavior

Module imports successfully by name.

Actual behavior

>> Import-Module ActiveDirectory
Import-Module: The specified module 'ActiveDirectory' was not loaded because no valid module file was found in any module directory.

Environment data


Name                           Value
----                           -----
PSVersion                      7.0.0-preview.6
PSEdition                      Core
GitCommitId                    7.0.0-preview.6
OS                             Microsoft Windows 10.0.18362
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0螕脟陋}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

CC @SteveL-MSFT

Area-SideBySide Committee-Reviewed Issue-Question Resolution-Fixed

All 21 comments

@scrthq can you please provide additional info:
1) $env:PSModulePath from your repro;
2) it feels like there is expectation that "C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules no longer being in $env:PSModulePath " is normal. Can you please clarify why do you have such expectation?
3) I was told that latest ActiveDirectory module is actually Core-compatible. To double-check this can you please copy-pase the contents of your C:\Windows\System32\WindowsPowerShell\v1.0\Modules\ActiveDirectory\ActiveDirectory.psd1?

The general rule is: If a module location is missing from $env:PSModulePath then Import-Module (and, subsequently, WinCompat) won't be able to find the module if the import is done by module name (as opposed to full path to the module).

@scrthq
I had a similar problem with $psmodulePath getting messed up in VS Code when I changed it in my profile. If I leave it alone it's fine. (see https://github.com/PowerShell/vscode-powershell/issues/2127#issuecomment-554586582 )

For completeness I tested these by putting ___ in front of the last part of the path in system environment variable so I could see what PowerShell did.

Windows PowerShell 5.1

%UserProfile%\Documents\WindowsPowerShell\Modules  << Even if it is in System Environment variable
Contents of system enviroment variable             << Normally includes %SystemRoot%\system32\WindowsPowerShell\v1.0\Modules
%ProgramFiles%\WindowsPowerShell\Modules           << Only if not in system environment variable. 

PowerShell 6.2.3

Ignores System Environment variable. 
$home\Documents\PowerShell\Modules
$env:ProgramFiles\PowerShell\Modules
$PsHome\Modules
$Env:systemRoot\system32\WindowsPowerShell\v1.0\Modules  <<Even if not in system environment variable

PowerShell 7 preview 6

$home\Documents\PowerShell\Modules
$env:ProgramFiles\PowerShell\Modules
$PsHome\Modules
C:\Program Files\WindowsPowerShell\Modules              <<Even if not in system environment variable      
C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules      <<Even if not in system environment variable
Contents of system environment variable if not already included AND path is valid

To clarify - yes, it works fine if I provide the full path to the module or add the System32\WindowsPowerShell path back to $env:PSModulePath -- that's not the point of the issue. Up until PowerShell 7-preview.5, C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules was included in the default values for $env:PSModulePath and it no longer appears to be that way in PowerShell 7-preview.6, resulting in importing modules in that path (e.g. ActiveDirectory, DNSServer, etc) to fail without taking additional steps to readd the path yourself. Existing scripts importing those modules running with PowerShell 7-preview.6 will subsequently break.


@anmenaga

  1. Here is the contents on PS 6.2.3 vs 7-preview.5 vs 7-preview.6 --- it looks like with 7-preview.6, the CurrentUser path for Windows PowerShell modules is swapped with the Machine path unexpectedly?

6.2.2

>> pwsh -noprofile -command "`$PSVersionTable.PSVersion.ToString();`$env:PSModulePath -split [IO.Path]::PathSeparator"
6.2.2
C:\Users\nferrell\Documents\PowerShell\Modules
C:\Program Files\PowerShell\Modules
c:\program files\powershell\6\Modules
C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules

7-preview.5

>> pwsh-preview -noprofile -command "`$PSVersionTable.PSVersion.ToString();`$env:PSModulePath -split [IO.Path]::PathSeparator"
7.0.0-preview.5
C:\Users\nferrell\Documents\PowerShell\Modules
C:\Program Files\PowerShell\Modules
c:\program files\powershell\7-preview\Modules
C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules

7-preview.6

>> pwsh-preview -noprofile -command "`$PSVersionTable.PSVersion.ToString();`$env:PSModulePath -split [IO.Path]::PathSeparator"
7.0.0-preview.6
C:\Users\nferrell\Documents\PowerShell\Modules
C:\Program Files\PowerShell\Modules
c:\program files\powershell\7-preview\Modules
C:\Users\nferrell\Documents\WindowsPowerShell\Modules
  1. As shown by the contents of $env:PSModulePath for previous versions, everything outside of a few early versions of PS Core 6 (if I remember correctly) contained C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules, hence the expectation for it to be present in 7-preview.6

  2. ActiveDirectory is indeed Core compatible and imports without issue if I provide either the full path or update the default $env:PSModulePath to include C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules again.


@jhoneill - Definitely not anything in my profile altering the PSModulePath. I don't think it's unreasonable to expect things that worked up to 7-preview.5 to continue working, so long as there weren't any breaking changes introduced.

Tried from another machine (new PC build) and I'm not able to reproduce there, strangely enough. I've tried installing via Chocolatey and the 64-bit MSI on the Releases page, both still have C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules in $env:PSModulePath as expected.

7-preview.6 reliably uninstalls/reinstalls on my current machine (work laptop) from the 64-bit MSI on the Release page and the same issue is present every time.

Both machines have consoles are launched in the same manner (Admin session within ConEmu), the only difference that stands out is I'm on Windows 10 Pro 1909 on the new PC build where it's working fine, but on Windows 10 Enterprise 1903 on the machine where the issue is present.

(a) Does it happen if you start with -NoProfile ?
and
(b) are there anything strange about
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\PSModulePath

[Sorry if you've already checked both of those]

@jhoneill - a) Yup, see the calls above at the start of each code block before the output of each block, e.g.:

>> pwsh-preview -noprofile -command "`$PSVersionTable.PSVersion.ToString();`$env:PSModulePath -split [IO.Path]::PathSeparator"

b) None of the items in that registry key are present in my $env:PSModulePath actually, so that may be something notable (e.g. that RegKey value is not being pulled in if it's expected to be pulled). Also worth noting that the other paths in that RegKey also do not show in $env:PSModulePath in other versions, so possibly a red herring.

Contents of the RegKey on the machine with the issue:

C:\ProgramData\Boxstarter;C:\Program Files\WindowsPowerShell\Modules;C:\windows\system32\WindowsPowerShell\v1.0\Modules;C:\opscode\chef-workstation\modules\

Contents of the RegKey on the machine that is working fine (note that this build is only a couple weeks old and is not my primary workstation):

%ProgramFiles%\WindowsPowerShell\Modules;%SystemRoot%\system32\WindowsPowerShell\v1.0\Modules

@scrthq
Hmmm. The one with boxstarter looks it re-wrote the entry because the %env% parts have been expanded. (Going back to DOS 3.1 I have a dim view of things which put themselves first on a path, so I'm going to blame boxstarter for everything :-)
AIUI if the value is missing PowerShell will re-create it so I might quickly export that bit of the registry and delete the key and see what happens.
I get something added to PS Module Path from the registry / global environment variable in 7 but not in 6. (See above).

@jhoneill - That makes sense re: Boxstarter (or Chef) expanding the env var values, but the resolved string would still be the same. Still an unexpected difference in experience between 7-p.5 and 7-p.6. I'm curious what would trip it up and add the CurrentUser path for WindowsPowerShell _instead of_ the Machine path on this machine.

I'm running into this problem on my Windows 10 Enterprise 1909 system, too, also starting with preview.6:

PS C:\Users\me> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      7.0.0-preview.6
PSEdition                      Core
GitCommitId                    7.0.0-preview.6
OS                             Microsoft Windows 10.0.18363
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0鈥
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

PS C:\Users\me> $env:PSModulePath -split [IO.Path]::PathSeparator
C:\Users\me\Documents\PowerShell\Modules
C:\Program Files\PowerShell\Modules
c:\program files\powershell\7-preview\Modules

Whereas with my default PowerShell:

PS C:\Users\me> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      5.1.18362.145
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.18362.145
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1


PS C:\Users\me> $env:PSModulePath -split [IO.Path]::PathSeparator

C:\Users\me\AppData\Local\Google\Cloud SDK\google-cloud-sdk\platform\PowerShell
C:\Program Files\WindowsPowerShell\Modules
C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\
C:\Program Files (x86)\Microsoft SQL Server\120\Tools\PowerShell\Modules\
C:\Program Files (x86)\Windows Kits\10\Microsoft Application Virtualization\Sequencer\AppvPkgConverter
C:\Program Files (x86)\Windows Kits\10\Microsoft Application Virtualization\Sequencer\AppvSequencer
C:\Program Files (x86)\Windows Kits\10\Microsoft Application Virtualization\

My HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\PSModulePat registry key value is

%SystemRoot%\system32\WindowsPowerShell\v1.0\Modules\;C:\Program Files (x86)\Microsoft SQL Server\120\Tools\PowerShell\Modules\;C:\Program Files (x86)\Windows Kits\10\Microsoft Application Virtualization\Sequencer\AppvPkgConverter;C:\Program Files (x86)\Windows Kits\10\Microsoft Application Virtualization\Sequencer\AppvSequencer;C:\Program Files (x86)\Windows Kits\10\Microsoft Application Virtualization\

For those of you that can repro this, can you start cmd.exe and run set psmodulepath and paste the output here? The change in PS7 Preview.6 is to inherit $env:PSModulePath rather than clobber it so unless the system32 path is explicitly removed from the registry, it should have been inherited.

C:\Users\me>set psmodulepath
PSModulePath=;C:\Users\me\AppData\Local\Google\Cloud SDK\google-cloud-sdk\platform\PowerShell

It's odd that it begins with a path separator. Maybe the Google Cloud SDK installer appended its own module path to an empty string (perhaps a logic error when PSModulePath isn't set)?

UPDATE: So that was a user variable. If I delete the PSModulePath user variable (using the System Properties > Environment Variables dialog), then in cmd.exe PSModulePath gets the value from the System variables value ...

C:\Users\me>set psmodulepath
PSModulePath=C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\;C:\Program Files (x86)\Microsoft SQL Server\120\Tools\PowerShell\Modules\;C:\Program Files (x86)\Windows Kits\10\Microsoft Application Virtualization\Sequencer\AppvPkgConverter;C:\Program Files (x86)\Windows Kits\10\Microsoft Application Virtualization\Sequencer\AppvSequencer;C:\Program Files (x86)\Windows Kits\10\Microsoft Application Virtualization\

and in PS7 preview.6:

PS C:\Users\me> $env:PSModulePath -split [IO.Path]::PathSeparator
C:\Users\me\Documents\PowerShell\Modules
C:\Program Files\PowerShell\Modules
c:\program files\powershell\7-preview\Modules
C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\
C:\Program Files (x86)\Microsoft SQL Server\120\Tools\PowerShell\Modules\
C:\Program Files (x86)\Windows Kits\10\Microsoft Application Virtualization\Sequencer\AppvPkgConverter
C:\Program Files (x86)\Windows Kits\10\Microsoft Application Virtualization\Sequencer\AppvSequencer
C:\Program Files (x86)\Windows Kits\10\Microsoft Application Virtualization\

image

So it does indeed behave like you're describing, @SteveL-MSFT, but it looks like it's inheriting the User scope env var if present and not the Machine scope:

image

@scrthq looks like it's inheriting the User scope env var if present and not the Machine scope - that is standard env var behavior as far as I know - User environment variables take precedence over system environment variables. (with the exception of Path env var, where values are combined)

Correct, if the user has defined their own PSModulePath env var, we respect that first then fall back to machine scope if available. So this seems like it's by-design.

I can understand that it's working how it's expected to, considering that it's looking at the environment variable now, but it doesn't change the fact that it alters the behavior on some systems.

Given the large amount of Windows PowerShell modules that specifically install to the Machine scope with no option to specify the User scope (e.g. modules that install through means other than Install-Module, such as ActiveDirectory / DNSServer / etc that currently install via RSAT installer or Windows Optional Feature, depending on the client OS), it feels like the System32 path should be ensured as present and added as last on the list of paths if not automatically added, or both the User and Machine paths should be concatenated.

Prior to Preview.6, pwsh did explicitly add the system32 path. However, this wasn't respecting the user's changes to the env var. The current behavior is consistent with cmd.exe which is to inherit the user env var, if available. Otherwise, use the machine scope env var, if available. Otherwise use a default env var. This should be consistent with other exes on Windows with other env vars. Special casing PSModulePath makes it inconsistent.

@SteveL-MSFT Correct me if I'm wrong (I very well may be), but the PATH environment variable has historically been treated specially, and is a concatenation of the user scope PATH and the machine scope PATH. This make sense -- the effective path ought to be a concatenation of the two.

Since the PSModulePath is also a list of directories to search, it seems like its semantics should be the same as PATH's. And indeed PowerShell 5.1, based on experimentation, treats both PATH and PSModulePath specially -- both are the concatenation of the respective user and machine variables. (Actually, I don't know if pwsh is doing this, or if the process is inheriting the already concatenated values).

It seems like the change in behavior introduced with pwsh Preview.6 is significant. I can imagine that a lot of existing software might depend on the previous behavior.

It is explicitly demo (one case from many) why I asked to change the old practice, do not change PSModulePath and use all path we need internally.

@PowerShell/powershell-committee reviewed this, except where $env:PSModulePath is not defined at all, pwsh should just use the $env:PSModulePath that is inherited as the OS will merge User and Machine env vars already.

If we merge them, save in env:PSModulePath and then change User and/or Machine env vars we again get an inconsistency that will amazes users..

:tada:This issue was addressed in #11276, which has now been successfully released as v7.0.0-rc.1.:tada:

Handy links:

Was this page helpful?
0 / 5 - 0 ratings