Skip to content

Get the default PSModulePath at runtime? #7082

Closed

Description

@rjmholt

Two questions on the PSModulePath in PowerShell Core:

  1. Is the the default setting of PSModulePath in PS Core documented anywhere (e.g. online) in a way that is general and cross platform? Like a table of what it is on each supported platform, or a string with unexpanded platform-specific variables in it? (e.g. $PSHOME/Modules:$AnotherPlace/SecondModuleDir)
  2. Is it possible to access the default PSModulePath at runtime? (Or to reset the current one?)

Activity

  1. added
    Issue-Questionideally support can be provided via other mechanisms, but sometimes folks do open an issue to get a
    WG-Enginecore PowerShell engine, interpreter, and runtime
    on Jun 15, 2018
  2. SteveL-MSFT commented on Jun 15, 2018

    @SteveL-MSFT
    Member

    This is probably a Documentation-Needed situation. Currently, you'll probably have to look at the code

  3. rjmholt commented on Jun 15, 2018

    @rjmholt
    CollaboratorAuthor

    It would also be nice to have it as an API endpoint perhaps (like a variable, static property, etc) -- like [ModuleIntrinsics]::GetModulePath() except without requiring arguments. Curious if anyone else has felt a need for that?

  4. SteveL-MSFT commented on Jun 15, 2018

    @SteveL-MSFT
    Member

    I think PSModulePath being an env var means you should get and set it like an env var

  5. mklement0 commented on Jun 17, 2018

    @mklement0
    Contributor

    What I feel is absent is not necessarily the built-in default value, but a way to access the various well-known module locations by scope.

    By analogy, let's take the example of $PROFILE, which contains the well-known profile locations as named-for-the-scope NoteProperty members:

    # Sample output when run on macOS
    PS> $PROFILE | Get-Member -type noteproperty | Select Name, Definition
    
    Name                   Definition
    ----                   ----------
    AllUsersAllHosts       string AllUsersAllHosts=/usr/local/microsoft/powershell/6-preview/profile.ps1
    AllUsersCurrentHost    string AllUsersCurrentHost=/usr/local/microsoft/powershell/6-preview/Microsoft.PowerShell_profile.ps1
    CurrentUserAllHosts    string CurrentUserAllHosts=/Users/jdoe/.config/powershell/profile.ps1
    CurrentUserCurrentHost string CurrentUserCurrentHost=/Users/jdoe/.config/powershell/Microsoft.PowerShell_profile.ps1

    I think we should provide something similar for module locations, using scopes such as AllUsers, CurrentUser, System, and Custom (the latter comprising all $env:PSModulePath entries in addition to the well-known locations).

    I'm unclear on how best to surface this functionality, however.

    In theory, we could use the same approach as with $PROFILE: decorate the $env:PSModulePath [string] value with NoteProperty members, but that is somewhat obscure.

    Then again, if properly documented, perhaps that's the way to go.

  6. SteveL-MSFT commented on Jun 18, 2018

    @SteveL-MSFT
    Member

    Bruce Payette (@BrucePay) you should take Michael Klement (@mklement0)'s suggestion as part of your PSModulePath6 RFC

  7. fsackur commented on Oct 31, 2018

    @fsackur

    I have submitted MicrosoftDocs/PowerShell-Docs#3214 to add the *nix locations to the existing about_Modules and about_Environment_Variables docs. I grant that this is only a partial solution.

  8. rjmholt commented on Nov 7, 2018

    @rjmholt
    CollaboratorAuthor

    Want to add that this may be of use in hosted PowerShell contexts where the hosting implementation might have reasons for using different paths for each module scope. (I would say "or possibly define its own", but I think we're coupled to the existing scopes now.)

    It might merit being able to set the scoped module paths in the powershell.config.json and exposing their location through some PowerShell API. (Like Michael Klement (@mklement0), I'm not sure what shape that API should be -- but I'd opt for either a variable or a method call, with the latter being a more direct fit to the current implementation).

  9. rjmholt commented on Nov 7, 2018

    @rjmholt
    CollaboratorAuthor

    In case anyone wants this btw, here's a quick function to get the defaults:

    function Get-DefaultModulePath
    {
        param(
            [Parameter()]
            [ValidateSet('AllUsers', 'CurrentUser', 'System', 'WindowsPSSystem')]
            [string[]]
            $Scope
        )
    
        if ($Scope -eq 'WindowsPSSystem')
        {
            if ($IsLinux -or $IsMacOs)
            {
                throw "The scope '$Scope' does not exist on non-Windows platforms"
            }
    
            if ($PSVersionTable.PSVersion -le '6.0')
            {
                throw "The scope '$Scope' is only valid in PowerShell versions 6.1 and above"
            }
        }
    
        if (-not $Scope)
        {
            $Scope = 'AllUsers','CurrentUser','System'
            if (-not ($IsLinux -or $IsMacOS))
            {
                $Scope += 'WindowsPSSystem'
            }
        }
    
        $invokeModuleMethod = {
            $bindingFlags = [System.Reflection.BindingFlags]'static,nonpublic'
            $m = [System.Management.Automation.ModuleIntrinsics].GetMethod($args[0], $bindingFlags)
            $m.Invoke($null, @())
        }
    
        $paths = @{}
        switch ($Scope)
        {
            'CurrentUser'
            {
                $p = & $invokeModuleMethod 'GetPersonalModulePath'
                $paths.CurrentUser = $p
            }
    
            'AllUsers'
            {
                $p = & $invokeModuleMethod 'GetSharedModulePath'
                $paths.AllUsers = $p
            }
    
            'System'
            {
                $p = & $invokeModuleMethod 'GetPSHomeModulePath'
                $paths.System = $p
            }
    
            'WindowsPSSystem'
            {
                $p = & $invokeModuleMethod 'GetWindowsPowerShellPSHomeModulePath'
                $paths.WindowsPSSystem = $p
            }
        }
    
        return $paths
    }
  10. fsackur commented on Nov 13, 2018

    @fsackur

    Rob Holt (@rjmholt) thanks very much for this, I'm using it in a module and, if I can get it working nicely, I'll publish it as a standalone script.

    I note that on 5.1 and 6.0.1 on WIndows, and 6.1 on Linux, there is no GetWindowsPowerShellPSHomeModulePath method; it is only available on 6.1 on WIndows, AFAICS.

  11. rjmholt commented on Nov 13, 2018

    @rjmholt
    CollaboratorAuthor

    Oh good point. Yes, I wrote that method for 6.1. Windows PowerShell should have some method like that in it, but I don’t know what it is. The method I wrote is equivalent to just ”${env:windir}\System32\WindowsPowerShell\v1.0\Modules”

  12. mklement0 commented on Nov 13, 2018

    @mklement0
    Contributor

    Thanks for the handy function, Rob Holt (@rjmholt) (also interesting to see the use of reflection to call the non-public methods - I hope we won't need that in the long run).

    It seems that in Windows PowerShell only the method that returns the CurrentUser scope exists, so I've amended your function to add fallback values for Windows PowerShell.
    (I've also changed the name to Get-StandardModulePath, shortened WindowsPSSystem to WinPSSystem and I'm using an ordered hashtable for the result, which makes the function v3+; btw, you forgot to rename a couple of instances of WindowsSystem to WindowsPSSystem in your recent edit).

    #requires -version 3
    function Get-StandardModulePath {
      param(
        [Parameter()]
        [ValidateSet('AllUsers', 'CurrentUser', 'System', 'WinPSSystem')]
        [string[]]
        $Scope
      )
    
      if ($Scope -eq 'WinPSSystem' -and ($IsLinux -or $IsMacOs)) {
        throw "The scope '$Scope' does not exist on non-Windows platforms."
      }
    
      if (-not $Scope) {
        $Scope = 'CurrentUser', 'AllUsers', 'System'
        if (-not ($IsLinux -or $IsMacOS)) {
          $Scope += 'WinPSSystem'
        }
      }
    
      $invokeModuleMethod = {
        $bindingFlags = [System.Reflection.BindingFlags]'static,nonpublic'
        $m = [System.Management.Automation.ModuleIntrinsics].GetMethod($args[0], $bindingFlags)
        $m.Invoke($null, @())
      }
    
      $paths = [ordered] @{}
      switch ($Scope) {
        'CurrentUser' {
          $paths.CurrentUser = try {
            & $invokeModuleMethod 'GetPersonalModulePath'
          }
          catch {
            # WinPS fallback.
            "$(Split-Path $PROFILE)\Modules"
          }
        }
    
        'AllUsers' {
          $paths.AllUsers = try {
            & $invokeModuleMethod 'GetSharedModulePath'
          }
          catch {
            # WinPS fallback.
            "$env:ProgramFiles\WindowsPowerShell\Modules"
          }
        }
              
        'System' {
          $paths.System = try {
            & $invokeModuleMethod 'GetPSHomeModulePath'
          }
          catch {
            # WinPS fallback.
            "$PSHOME\Modules"
          }
        }
    
        'WinPSSystem' {
          $paths.WinPSSystem = try {
            & $invokeModuleMethod 'GetWindowsPowerShellPSHomeModulePath'
          }
          catch {
            # WinPS fallback.
            "$PSHOME\Modules"
          }
        }
      }
    
      return $paths
    }
  13. rjmholt commented on Nov 14, 2018

    @rjmholt
    CollaboratorAuthor

    Michael Klement (@mklement0) Yeah I opened this issue because I think those methods (or something like them) should really be a public API, since they are a contract with PSGet

  14. mklement0 commented on Nov 14, 2018

    @mklement0
    Contributor

    Ah, of course, Rob Holt (@rjmholt); so another thank you for opening this issue.

  15. rjmholt commented on Nov 28, 2019

    @rjmholt
    CollaboratorAuthor

    Steve Lee (@SteveL-MSFT) is there a reason #11057 is marked as closing this issue? I couldn't find any new mechanism to solve feature request in that PR

  16. JustinGrote commented on Sep 4, 2024

    @JustinGrote
    Contributor

    Since this was erroneously closed, #15552 is probably the best location to continue discussion.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Area-Maintainers-Documentationspecific to documentation in this repoIssue-Questionideally support can be provided via other mechanisms, but sometimes folks do open an issue to get aResolution-No ActivityIssue has had no activity for 6 months or moreWG-Enginecore PowerShell engine, interpreter, and runtime

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions