Repository navigation
Allow to specify custom module path in Install-PSResource #1494
Description
Activity
- addedIssue-EnhancementNew feature or requestNew feature or request
on Nov 16, 2023 cc Sydney Smith (@SydneyhSmith) since this seems to be a PSResourceGet issue.
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.
Reacted by Sydney Smith, Adam Daniel, Olav Rønnestad Birkeland, ccatlett1984, Sam Erde, Aaron Meyers and Bryan Hoango-l-a-v commented
on Apr 4, 2024 ContributorMore actionsKirill 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-Modulewill work.Reacted by Sam Erde and jgarciaGit29Olav 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 Azand 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, ..
Reacted by Olav Rønnestad Birkeland, soredake, John Ericson, ccatlett1984, Sam Erde, Daniel Niccoli, Shmueli Englard, jgarciaGit29, Bryan Hoang, Tomáš Kapin and 1 moreo-l-a-v commented
on Apr 4, 2024 ContributorMore actionsOops, 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").
Olav Rønnestad Birkeland (@o-l-a-v)
This is how PowerShell have worked since forever.
This module
PSResourceGetwould 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?
Reacted by Matthew ParsonsThis is blocking PowerShell/PowerShell/issues/15552 from moving forward.
For far longer than PowerShell has been around, the
localappdataandprogramdatafolders have been the standard way to store application data for user and computers.Please, make the change so that this can move on.
Reacted by soredake, Olav Rønnestad Birkeland, Cédric Menzi, Kirill Osenkov, Aaron Meyers and Bryan HoangReacted by Michal ZOBECFor far longer than PowerShell has been around, the
localappdataandprogramdatafolders 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
o-l-a-v commented
on Jul 12, 2024 ContributorMore actionsHow about a new environment variable for given scope (AllUsers vs. CurrentUser), say
PSResourceGetInstallPathOverride, that can be set with a new cmdletSet-PSResourceInstallPathOverride -Path <path> -Scope <CurrentUser|AllUsers>, which then should be used if present byInstall-PSResourceandUpdate-PSResourcewith 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
PSModulePathenv 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
PSModulePathinpowershell.config.jsonwith Windows PowerShell 5.1It'd probably be the best option if it also worked for Windows PowerShell 5.1.
About this option here:
Suggested here:
- 2021-08-12 Let's move PSModulePath out of the documents folder PowerShell#15552 (comment)
- 2024-08-20 Allow to specify custom module path in Install-PSResource #1494 (comment)
- 2024-08-20 Let's move PSModulePath out of the documents folder PowerShell#15552 (comment)
- 2024-08-22 Allow to specify custom module path in Install-PSResource #1494 (comment)
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?
- Warn that this directory is empty, require confirmation to proceed? Override with
- It won't make PowerShell able to import module or use script as they will not be added to
PSModulePathandPATH.- Or should
Install-PSResource -Pathjust fix that too automagically?
- Or should
Proposal
New environment variable
PSResourceGetInstallPathOverride- Can only contain one path.
- Automatic subdirectories
\Modulesand\Scripts. - If
PSResourceGetInstallPathOverrideexist in the scope thatInstall-PSResourceorUpdate-PSResourceis run: Use it. Else use default behavior.
New cmdlet
Set-PSResourceInstallPathOverride- Syntax:
Set-PSResourceInstallPathOverride -Path '<valid_path>' -Scope 'CurrentUser/AllUsers' - Logic:
- If running as administrator and
-Scope CurrentUser: Throw, else next step does not make sense. - Try to create directory if it does not already exist. Don't continue if it fails.
- Also create subdirectories
\Modulesand\Scripts - If all directories already exist, try to create and delete a dummy directory to make sure we have sufficient permissions.
- Also create subdirectories
- Set
PSResourceGetInstallPathOverrideenvironment variable in given scope. - Add
PSResourceGetInstallPathOverride\ModulestoPSModulePathandPSResourceGetInstallPathOverride\ScriptstoPATHin given scope to ensure PowerShell will be able to find the resources.
- If running as administrator and
- 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(+\Modulesand\Scripts) if present and directory exists.
Reacted by Joel BennettReacted by Cédric Menzi, Gilbert Sanchez, Bennett Blodinger and Adam Danielo-l-a-v commented
on Jul 13, 2024 ContributorMore actionsI've started a draft PR ^ where the basics already work:
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.
Reacted by Adam DanielReacted by Gilbert Sanchez and Julian PawlowskiI 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 $Profilewith a "PSModulePath" use that for "CurrentUser" ...If there is a powershell.config.json in in
$PSHomewith a "PSModulePath" use that for "AllUsers" ...Those values will be in the PSModulePath, unless the user removes them, after startup.
Reacted by Ajay Mehta, Julian Pawlowski, Fredrik Forséll, Olav Rønnestad Birkeland, Sam Erde, Robert Klohr, Bryan Hoang and turquoise-turtleo-l-a-v commented
on Aug 21, 2024 ContributorMore actionsSetting 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?
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
$PSHomeand one inSplit-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;PATH4Now 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;PATH4Finally, 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 $pathAnd start a final PowerShell instance (you may want to run it
-noprofilebecause 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;PATH4Hopefully 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.
Reacted by Olav Rønnestad Birkeland, Bryan Hoang and Tomáš KapinReacted by Julian Pawlowski and Sam Erdeo-l-a-v commented
on Aug 22, 2024 ContributorMore actionsOh, 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.
Reacted by Julian PawlowskiJoel 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!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\Modulesso that's what I have mine set to...Reacted by Julian PawlowskiJustinGrote commented
on Sep 4, 2024 ContributorMore actionsFor 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:
- 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)
- Defaults to CurrentUser unless explicitly meant for AllUsers (since AllUsers almost certainly requires admin rights so it shouldn't be the default target)
Reacted by Julian PawlowskiFriedrichWeinmann commented
on May 7, 2025 ContributorMore actionsObviously 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
Reacted by Olav Rønnestad BirkelandReacted by Shmueli EnglardI 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.
Reacted by Cédric Menzi, Justin Grote, fomlhaut and Michal ZOBECTomáš 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.
Reacted by Tomáš KapinReacted by Adam Daniel
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