Skip to content

Allow to specify custom module path in Install-PSResource #1494

Description

@cmenzi

Summary of the new feature / enhancement

Install-Module and now also Install-PSResourceGet do not allow to specify the Path where Modules are installed. It only works via Save-Module or Save-PSResource.

It would be great if there is a environment variable or parameter to control where powershell modules are installed when using Scope CurrentUser

Proposed technical implementation details (optional)

No response

Activity

  1. StevenBucher98 commented on Dec 18, 2023

    @StevenBucher98

    cc Sydney Smith (@SydneyhSmith) since this seems to be a PSResourceGet issue.

  2. KirillOsenkov commented on Apr 4, 2024

    @KirillOsenkov

    Sydney Smith (@SydneyhSmith) would it be possible to proritize this? I'm from Azure Data and we have very significant support costs in our org related to modules being in OneDrive. I can also reach out internally if needed.

  3. o-l-a-v commented on Apr 4, 2024

    @o-l-a-v
    Contributor

    Kirill Osenkov (@KirillOsenkov)

    There is also the Save-PSResource -IncludeXml -Path 'your\custom\path' cmdlet, it already does this.

    As long as the path is in PSModulePath Import-Module will work.

  4. cmenzi commented on Apr 4, 2024

    @cmenzi
    Author

    Olav Rønnestad Birkeland (@o-l-a-v) Yes, this is also what I mentioned in the issue. But nobody will remember this all the time.

    You usually go to PowerShell Gallery and copy&paste the install-psresource Az and run it. There are also scripts, where somebody ensures that a certain module is installed.

    I would really prefer an environment variable or a one-time setting Set-PSDefaultResourceLocation, ..

  5. o-l-a-v commented on Apr 4, 2024

    @o-l-a-v
    Contributor

    Cédric Menzi (@cmenzi)

    Oops, yep, also mentioned in 1st post.

    This is how PowerShell have worked since forever. We've requested the ability to choose path for years. I don't see it happening any time soon. Thus Save-PSResource and set PSModulePath is currently the best workaround IMO.

    I'd like PSResourcePath to just use the first path in PSModulePath in user context if choosing CurrentUser as scope (and PSModulePath env variable in system context if scope "Machine").

  6. cmenzi commented on Apr 4, 2024

    @cmenzi
    Author

    Olav Rønnestad Birkeland (@o-l-a-v)

    This is how PowerShell have worked since forever.

    This module PSResourceGet would have been the chance to change that, isn't it :-). I mean it's something new that could also behave it bit different. I mean every command name has change from Install-Module to Install-PSResource, ...

    Why not changing a bit the behavoir or adding one parameter?

  7. benwa commented on Jul 9, 2024

    @benwa

    This is blocking PowerShell/PowerShell/issues/15552 from moving forward.

    For far longer than PowerShell has been around, the localappdata and programdata folders have been the standard way to store application data for user and computers.

    Please, make the change so that this can move on.

  8. BlackV commented on Jul 12, 2024

    @BlackV

    For far longer than PowerShell has been around, the localappdata and programdata folders have been the standard way to store application data for user and computers.

    In windows, there is more than windows to think about with a change like this

  9. o-l-a-v commented on Jul 12, 2024

    @o-l-a-v
    Contributor

    How about a new environment variable for given scope (AllUsers vs. CurrentUser), say PSResourceGetInstallPathOverride, that can be set with a new cmdlet Set-PSResourceInstallPathOverride -Path <path> -Scope <CurrentUser|AllUsers>, which then should be used if present by Install-PSResource and Update-PSResource with fallback to current default behavior?

    Easy to implement. Should not introduce breaking changes. Don't have to wait for PowerShell to change anything. More reasoning:

    Click to view

    Considerations

    Changing default behavior is breaking

    • After almost two decades (PowerShell first appeared in November 2006) with current default behavior, changing the default behavior of how and where PowerShell modules and scripts are installed will be a breaking change with unknown consequences.
    • Years and years of documentation, Reddit (and forum posts) and blogs will suddenly be both deprecated and misleading for new users.
    • How would we test this breaking change? When would we be happy with the test data and make the decision to change default behavior?
    • Even though most agree "MyDocuments" isn't a suitable place to store PowerShell modules, we should try to avoid breaking changes with the consequences already mentioned.

    Current default behavior is worse for Windows than Unix

    • Default install path for scripts and modules on Unix is not MyDocuments.
    • But the ability to override default path would be nice for Unix too.

    Can't reliably use PSModulePath env variable for override

    • Can contain multiple paths. How to choose what path to use? First index (see next bullet point)? Alphabetical?
    • Other 3rd parties like Scoop adds their path to the front of PSModulePath, so can't blindly choose the first path.
      • [System.Environment]::GetEnvironmentVariable('PSModulePath','User').Split([System.IO.Path]::PathSeparator)[0].

    Can't use PSModulePath in powershell.config.json with Windows PowerShell 5.1

    It'd probably be the best option if it also worked for Windows PowerShell 5.1.

    About this option here:

    Suggested here:

    Confirmed it does not work for Windows PowerShell 5.1 here:

    No need for Install-PSResource -Path

    • It already exist in Save-PSResource -IncludeXml -Path.
    • Specifying path every time you install and update a module is not a good user experience.
      • Prone to errors.
      • What to do if a not yet used path is specified?
        • Warn that this directory is empty, require confirmation to proceed? Override with -Force?
        • If directory does not exist, should it be created?
    • It won't make PowerShell able to import module or use script as they will not be added to PSModulePath and PATH.
      • Or should Install-PSResource -Path just fix that too automagically?

    Proposal

    New environment variable PSResourceGetInstallPathOverride

    • Can only contain one path.
    • Automatic subdirectories \Modules and \Scripts.
    • If PSResourceGetInstallPathOverride exist in the scope that Install-PSResource or Update-PSResource is run: Use it. Else use default behavior.

    New cmdlet Set-PSResourceInstallPathOverride

    • Syntax: Set-PSResourceInstallPathOverride -Path '<valid_path>' -Scope 'CurrentUser/AllUsers'
    • Logic:
      1. If running as administrator and -Scope CurrentUser: Throw, else next step does not make sense.
      2. Try to create directory if it does not already exist. Don't continue if it fails.
        • Also create subdirectories \Modules and \Scripts
        • If all directories already exist, try to create and delete a dummy directory to make sure we have sufficient permissions.
      3. Set PSResourceGetInstallPathOverride environment variable in given scope.
      4. Add PSResourceGetInstallPathOverride\Modules to PSModulePath and PSResourceGetInstallPathOverride\Scripts to PATH in given scope to ensure PowerShell will be able to find the resources.
    • Rerun with same parameters: Repair the override by checking all changes are still present.

    Change to Utils.cs -> GetPathsFromEnvVarAndScope

    • Return value of environment variable PSResourceGetInstallPathOverride (+ \Modules and \Scripts) if present and directory exists.
  10. o-l-a-v commented on Jul 13, 2024

    @o-l-a-v
    Contributor

    I've started a draft PR ^ where the basics already work:

    image

    It can be tested by cloning the branch and build the module with for instance:

    & .\build.ps1 -Clean -Build -BuildFramework net472

    Then import the built DLL with for instance:

    Import-Module -Name 'C:\Users\olavb\Git\Others\PowerShell--PSResourceGet\out\Microsoft.PowerShell.PSResourceGet\Microsoft.PowerShell.PSResourceGet.dll'

    Waiting for some response from PSResourceGet maintainers before spending more time on it. I might try to add some more functionality while I wait.

  11. Jaykul commented on Aug 20, 2024

    @Jaykul
    Contributor

    I think it should just look at powershell.config.json (if it exists) and use the PSModulePath there (if it exists), and otherwise fall back to what it is now.

    If there is a powershell.config.json in Split-Path $Profile with a "PSModulePath" use that for "CurrentUser" ...

    If there is a powershell.config.json in in $PSHome with a "PSModulePath" use that for "AllUsers" ...

    Those values will be in the PSModulePath, unless the user removes them, after startup.

  12. o-l-a-v commented on Aug 21, 2024

    @o-l-a-v
    Contributor

    Joel Bennett (@Jaykul)

    https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_powershell_config?view=powershell-7.4#psmodulepath

    Setting PSModulePath in powershell.config.json says it "Overrides the PSModulePath settings for this PowerShell session.".

    Sounds to me you either set PSModulePath env variable, or as a setting in this JSON?

    That's not a great solution either. If it overrides PSModulePath the environment variable, then you'd potentially want multiple paths here too. Which path should PSResourceGet default to, the first one?

  13. Jaykul commented on Aug 22, 2024

    @Jaykul
    Contributor

    You're misunderstanding it, Olav Rønnestad Birkeland (@o-l-a-v) -- I mean, I'm not saying the docs are great, but just try it.

    Each config file (one in $PSHome and one in Split-Path $Profile) overrides one of the paths that PowerShell ADDS to the PSModulePath environment variable (for all future sessions). It's only used at the start of each session -- so if you change your PSModulePath in your profile (as I do), you might not even notice, but here's how it works if you don't have a profile:

    Set your PSModulePath environment variables to short strings we can identify:

    [System.Environment]::SetEnvironmentVariable("PSModulePath", "PATH3", "User")
    [System.Environment]::SetEnvironmentVariable("PSModulePath", "PATH4", "Machine")

    Start a new PowerShell instance (e.g. a new tab in Windows Terminal), the PSModulePath will be something like this:

    C:\Users\Jaykul\Documents\PowerShell\Modules;C:\Program Files\PowerShell\Modules;c:\program files\powershell\7\Modules;PATH3;PATH4
    

    Now set the user config file. It's important that you understand you can only put a single path to a folder in this string, to replace the native path (which would be a "Modules" folder adjacent to the config file). You can use other environment variables with %ComSpec% syntax, but you cannot put multiple folders with a path separator. For demonstration purposes, we'll again use a short string we can identify:

    $path = Join-Path (Split-Path $Profile) powershell.config.json
    $config = @{}
    if (Test-Path $path) {
       $config = Get-Content $path | ConvertFrom-Json -AsHashtable
    }
    $config.PSModulePath = "PATH1"
    $config | ConvertTo-Json | set-content $path

    And start a new PowerShell instance (e.g. a new tab in Windows Terminal), the PSModulePath will be something like this:

    PATH1;C:\Program Files\PowerShell\Modules;c:\program files\powershell\7\Modules;PATH3;PATH4
    

    Finally, just to finish the demo, set the PSHome config:

    $path = Join-Path $PSHOME powershell.config.json
    $config = @{}
    if (Test-Path $path) {
       $config = Get-Content $path | ConvertFrom-Json -AsHashtable
    }
    $config.PSModulePath = "PATH2"
    $config | ConvertTo-Json | set-content $path
    

    And start a final PowerShell instance (you may want to run it -noprofile because it's going to be super broken, with no modules available, including PSReadLine). The PSModulePath will be:

    PATH1;C:\Program Files\PowerShell\Modules;PATH2;PATH3;PATH4
    

    Hopefully it's clear how those configs interact with the environment variables, and why reading them makes sense (with a fallback to the default of a "Modules" folder next to the config file path, if they're not set).

    Incidentally, I find it really weird that the only path that cannot be overridden points at a folder that doesn't even exist on my systems.

  14. o-l-a-v commented on Aug 22, 2024

    @o-l-a-v
    Contributor

    Oh, okay. Thats nice. And should work the same on all platforms. I should've tried rather than trusting the docs. Thanks for the very detailed explaination Joel Bennett (@Jaykul). 😊

    Edit: But it can't be used with Windows PowerShell 5.1, which PSResourceGet also supports.

  15. OranguTech commented on Aug 29, 2024

    @OranguTech

    Joel Bennett (@Jaykul) - Yes thank you, I've banged my head against that before: it looks like it works at first, namely that the first entry in $PSModulePath is changed, but installing a module still installs to the old, default path, but now import-module can't find it.
    (The docs even warn that the powershellget commandlets don't pay attention to this setting)

    IMO this makes it worse-than-useless (because it breaks existing functionality for no trade-off). If you can get the rest of the ecosystem to honor powershell.config.json (and get the docs updated!), please do!

  16. Jaykul commented on Aug 30, 2024

    @Jaykul
    Contributor

    Matthew Parsons (@OranguTech) wrote:

    IMO this makes it worse-than-useless (because it breaks existing functionality for no trade-off). If you can get the rest of the ecosystem to honor powershell.config.json (and get the docs updated!), please do!

    That's definitely my goal 😉

    There's an issue in the ModuleFast repo too. They are not following this setting yet, but they do (by default) install the modules in %LOCALAPPDATA%\powershell\Modules so that's what I have mine set to...

  17. added a commit that references this issue on Aug 31, 2024
  18. JustinGrote commented on Sep 4, 2024

    @JustinGrote
    Contributor

    For reference I've made a function that will be incorporated into moduleFast that provides how I envision PowerShell should be providing the info, hopefully a similar PR will reveal a public API for this.
    PowerShell/PowerShell#15552 (comment)

    In summary and testing:

    1. Changing both system and user powershell.config.json results in both paths being modified to PSModulePath, however system always still keeps the original AllUsers ModulePath, while the CurrentUser module path gets replaced (this is desirable behavior e.g. to get away from OneDrive)
    2. Defaults to CurrentUser unless explicitly meant for AllUsers (since AllUsers almost certainly requires admin rights so it shouldn't be the default target)
  19. FriedrichWeinmann commented on May 7, 2025

    @FriedrichWeinmann
    Contributor

    Obviously not a solution to the core issue, but tired with this issue and many others, I've implemented a standalone solution to this:

    https://github.com/PowershellFrameworkCollective/PSFramework.NuGet

    Allows defining your own scopes or overriding the default ones. Scopes can be static paths or dynamically calculated paths.
    And you can bootstrap it without needing PowerShellGet:

    iwr https://raw.githubusercontent.com/PowershellFrameworkCollective/PSFramework.NuGet/refs/heads/master/bootstrap.ps1 | iex

    And then:

    Register-PSFModuleScope -Name Personal -Path C:\code\Modules -Description 'Personal local modules, not redirected to OneDrive documents or to some network share'
    Install-PSFModule -Name EntraAuth -Scope Personal

    Or:

    # Once only
    Register-PSFModuleScope -Name CurrentUser -Path C:\code\Modules -Description 'Personal local modules, not redirected to OneDrive documents or to some network share' -Persist
    
    # From then on (including in all future consoles)
    Install-PSFModule -Name EntraAuth
  20. tkapin commented on Feb 7, 2026

    @tkapin

    I came across this issue after spending a couple of hours trying to configure PowerShell (7.5.4) to install modules to a custom local path. In my environment, the user's Documents directory is redirected to OneDrive by company policy, which makes the default install location unsuitable for module management.

    I would really appreciate if the install path could be driven by an environment variable or a configurable setting. The current experience of working around this limitation is quite cumbersome, and I believe a straightforward configuration option would significantly improve usability for users in similar enterprise environments.

  21. kilasuit commented on Feb 9, 2026

    @kilasuit

    Tomáš Kapin (@tkapin) good news as this is coming in a future version of PowerShell once this PR is merged and a version with it included is released. This has been a multi year headache that should be soon not an issue.

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions