Skip to content

Add PSContentPath Infrastructure#26509

Open
jshigetomi wants to merge 52 commits into
PowerShell:masterfrom
jshigetomi:PSModulePathFix
Open

Add PSContentPath Infrastructure#26509
jshigetomi wants to merge 52 commits into
PowerShell:masterfrom
jshigetomi:PSModulePathFix

Conversation

@jshigetomi
Copy link
Copy Markdown
Collaborator

@jshigetomi jshigetomi commented Nov 21, 2025

PR Summary

This pull request introduces new user-facing cmdlets for managing the PowerShell content path, refactors how personal module and configuration paths are determined, and improves cross-platform consistency for configuration storage. The most significant changes are the addition of Get-PSContentPath and Set-PSContentPath cmdlets, updates to the logic for resolving user content/config paths (especially on Windows), and the adoption of the content path as the basis for the personal module path.

Platform-specific content/config path handling:

  • Introduced DefaultPSContentDirectory and LocalAppDataPSContentDirectory properties in CorePsPlatform.cs to standardize the location of PowerShell content data across platforms. On Windows, this now prefers LocalAppData\PowerShell, with a fallback for legacy locations.
  • Refactored PowerShellConfig to select the user config directory/file based on the presence of existing config files, ensuring backward compatibility while defaulting to the new location for new installs.

Personal module path changes:

  • Changed the logic for determining the personal module path to use the PS content path (from GetPSContentPath()), rather than hardcoded locations like My Documents or XDG directories.
  • Updated the module path resolution to fall back to the new personal module path if not explicitly set in config.

Constants and documentation improvements:

  • Added a constant for the PS user content path config key in ModuleIntrinsics.cs.
  • Clarified documentation for module constraint matching.

New cmdlets for PSContentPath management:

  • Added Get-PSContentPath and Set-PSContentPath cmdlets, allowing users to query and configure the PowerShell content path with validation logic.
  • Registered these cmdlets in the core session state, making them available by default. [1] [2]

Get-PSContentPath

Gets the current PowerShell user content path (where modules, scripts, help, and profiles are stored). Returns a System.IO.DirectoryInfo object with a ConfigFile NoteProperty attached, which the default list formatter displays.

# Get the current content path
(Get-PSContentPath).FullName

# Use in string interpolation
"Modules are stored in $(Get-PSContentPath)\Modules"

# Get the config file location
(Get-PSContentPath).ConfigFile

Set-PSContentPath

Sets a custom content path, persisted in powershell.config.json. The confirm impact is High, so users are prompted for confirmation by default.

Parameter Description
-Path <string> Absolute path for the content directory (env vars expanded)
-Default Resets to platform default
-WhatIf / -Confirm ShouldProcess support (prompts by default due to High impact)
PS> Set-PSContentPath -Path "D:\PowerShell"

Confirm
Are you sure you want to perform this action?
...
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"):

PS> Set-PSContentPath -Path "$env:OneDrive\PowerShell"
PS> Set-PSContentPath -Default

$PSUserContentPath Variable

A readonly automatic variable exposing the current content path. Users cannot modify it directly—they get a helpful error directing them to use Set-PSContentPath.

PS> $PSUserContentPath
C:\Users\user\Documents\PowerShell

PS> $PSUserContentPath = "D:\Other"
InvalidOperation: Cannot modify the readonly variable 'PSUserContentPath'. 
Use Set-PSContentPath to configure this value.

PR Context

This change represents a major milestone for the PowerShell ecosystem—one that has been long requested by the community, and discussed extensively. The feature originated from community feedback in #15552, aligns with related work in PowerShell/PSResourceGet#1912, and was shaped through detailed specifications in PowerShell/PowerShell-RFC#388.

This feature lays the foundation for flexible, user-configurable PowerShell content storage, addressing scenarios such as roaming profiles and containerized environments.

By enabling customization of content paths, we empower users to adapt PowerShell to modern deployment and security requirements.

PR Checklist

@jshigetomi jshigetomi added the CL-Experimental Indicates that a PR should be marked as an Experimental Feature in the Change Log label Nov 21, 2025
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces an experimental feature called PSContentPath that changes the default location for PowerShell user content (modules, scripts, help, and profiles) from Documents\PowerShell to LocalAppData\PowerShell on Windows, and provides a configurable path via Get-PSContentPath and Set-PSContentPath cmdlets. The feature includes lazy migration logic to move config files from the old location to the new location when the experimental feature is enabled.

Key changes:

  • Adds experimental feature PSContentPath with new cmdlets to get/set the content path
  • Implements lazy migration from old config directory to new default location (LocalAppData)
  • Updates all content path references to use the centralized Utils.GetPSContentPath() API

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
ExperimentalFeature.cs Adds PSContentPath experimental feature definition
GetSetPSContentPathCommand.cs New cmdlets for getting and setting PSContentPath with validation
PSConfiguration.cs Adds GetPSContentPath/SetPSContentPath methods and lazy migration logic
Utils.cs Adds GetPSContentPath() helper method with fallback logic
CorePsPlatform.cs Adds DefaultPSContentDirectory constant for the new default location
ModuleIntrinsics.cs Updates GetPersonalModulePath and SetModulePath to use new API
HostUtilities.cs Updates profile path logic to use GetPSContentPath
HelpUtils.cs Updates help search path to use GetPSContentPath
InitialSessionState.cs Registers new Get/Set-PSContentPath cmdlets

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/System.Management.Automation/engine/PSConfiguration.cs Outdated
Comment thread src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs
Comment thread src/System.Management.Automation/engine/Utils.cs Outdated
Comment thread src/System.Management.Automation/CoreCLR/CorePsPlatform.cs Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@iSazonov
Copy link
Copy Markdown
Collaborator

If user intention is to migrate content to new location I believe the new cmdlets should help them.
I'd like to get not only current content path but size too Get-PSContentPath -MeasureSize.

@kilasuit
Copy link
Copy Markdown
Collaborator

kilasuit commented Nov 23, 2025

@jshigetomi - Please mark this PR as a Draft & go through the PR checklist & mark things appropriately add some PR Context as is requested in the PR template.

Until all thats done we shouldn't look to accept this pr.

You can do better than this & should do so in future too.
@iSazonov re this
I'd like to get not only current content path but size too Get-PSContentPath -MeasureSize.
I don't disagree on having that ability too.

@jshigetomi jshigetomi marked this pull request as draft November 23, 2025 04:23
@jshigetomi
Copy link
Copy Markdown
Collaborator Author

@kilasuit - Thanks for calling me out on that. Definitely intended this as a draft.

@microsoft-github-policy-service microsoft-github-policy-service Bot added the Waiting on Author The PR was reviewed and requires changes or comments from the author before being accept label Nov 26, 2025
@microsoft-github-policy-service microsoft-github-policy-service Bot removed the Waiting on Author The PR was reviewed and requires changes or comments from the author before being accept label Dec 1, 2025
@kilasuit
Copy link
Copy Markdown
Collaborator

kilasuit commented Feb 9, 2026

@jshigetomi I'm coming back to this & am reviewing right now & wanna address some bits prior to a proper code review.

I noticed earlier you had mentioned

Added a cmdlet Move-PSContent that will move or copy things over to the new location.

I see this was removed since then and that copilot was concerned about data loss which we can't 100% manage as well all sorts of things can happen that can cause data loss.

However, this concern "could" be worked around by having an export/import mechanism (as opposed to a move) by making use of archiving methods maybe by using the Microsoft.PowerShell.Archive module or native with an Export-PSContent cmdlet and an Import-PSContent cmdlet duo which I think would enable 2 important scenarios in my mind

  1. Backups/Restores
  2. Preseeding on new environments that are disconnected or just have major network constraints like bad internet connectivity / low data limits.

Tne Preseeding scenario also allows for smaller OS images too which is a good thing. Which I can envison being part of some orgs DSC configurations as I know it would be part of any future ones that I would use whether it be to build an OS/Container Image. This also supports being able to do hashing checks for integrity purposes.

That's something that I'd like to see we have however I'm 50/50 on whether these Cmdlets should be in PSResourceGet or included as part of PowerShell. There's benefits having them in both places & infact I think that it may actually be sensible to have them in both, at least for a short period of time.

Also we need to be mindful of not overengineering this just because we can, however as part of the Import process, on completion it optionally could remove any existing data from the old location as we have to be mindful of some of the 7 & 5.1 differences and just leave this up to the user/admins to manage.

Move-PSContentPath -Destination is limited to only 2 destinations, OneDrive or LocalAppData. This is to ensure PowerShell can find the user config path on startup.

I don't see this being needed with your Set-PSContentPath cmdlet so kinda glad this has gone from how this is right now for review.

Then I don't understand at all how and why you are going to "migrate". In fact, the old scheme will coexist with the new one. While the updated engine will support the new scheme, the user could roll back from PSResourceGet to PowerShellGet for some reason.
So I'm sure the only thing that needs to be done here is to update the engine to process the new path in the first place and that's it.
Otherwise, we will just create a lot of headaches for users, although in fact, the vast majority of them do not care where the modules are located.

Whilst I don't like it, PowerShellGet 1.0.0.1 & other versions of that module could potentially be updated to check for an environment variable or reg key entry that points to the new PSContentPath which I think would mitigate @iSazonov concerns here.

I agree. That's what we are doing for Windows PowerShell in pwsh. We kept both and placed pwsh first.

What do you mean by this?

@jshigetomi
Copy link
Copy Markdown
Collaborator Author

jshigetomi commented Feb 9, 2026

@kilasuit

I agree. That's what we are doing for Windows PowerShell in pwsh. We kept both and placed pwsh first.

What do you mean by this?

When pwsh 6 came out it moved the modules directory from Documents\WindowsPowerShell to Documents\PowerShell. For that transition we added the Documents\PowerShell module folder to $env:PSModulePath first while keeping Documents\WindowsPowerShell.

And for removing Move-PSContentPath, I think it would be best in another PR so we don't bloat this one up more than it already is. But yes I do like your ideas. Once this is in I will work on creating a nice migration tool of some sort.

@kilasuit
Copy link
Copy Markdown
Collaborator

kilasuit commented Feb 11, 2026

Only other small comment atm is would it be best to just have all -PSContent cmdlets in
src/System.Management.Automation/engine/Configuration/PSContentCommands.cs
as opposed to
src/System.Management.Automation/engine/Configuration/PSContentPathCommands.cs
&
src/System.Management.Automation/engine/Configuration/PSContentCommands.cs etc etc

@TobiasPSP TobiasPSP moved this from In-Progress-PullRequests to Reviewed in Cmdlets Working Group Feb 18, 2026
@SteveL-MSFT
Copy link
Copy Markdown
Member

The @PowerShell/wg-powershell-cmdlets discussed the cmdlets. For Get-PSContentPath we don't need the -Size switch nor the -ConfigFile switch. Size information shouldn't be returned. For -ConfigFile, the cmdlet should return a System.IO.DirectoryInfo object, but add a NoteProperty that contains the ConfigFile and the default formatter for list view can display that. For Set-PSContentPath, -Reset should be called -Default and the confirm impact for this cmdlet should be High so that it prompts by default.

@SteveL-MSFT SteveL-MSFT added WG-Reviewed A Working Group has reviewed this and made a recommendation and removed WG-NeedsReview Needs a review by the labeled Working Group labels Feb 18, 2026
@jshigetomi jshigetomi force-pushed the PSModulePathFix branch 3 times, most recently from 198cfc3 to 95ec9f8 Compare March 3, 2026 15:59
@kilasuit
Copy link
Copy Markdown
Collaborator

kilasuit commented Mar 8, 2026

@jshigetomi can we please update PR Summary with the changes since Steve's comments from the Working Group above which FYI I wasn't able to attend the meeting where these comments came from.

Also just for people like @iSazonov reading this PR that wanted the Size to be returned as part of Get-PSContentPath (which I do agree it would be a really nice to have) you can get this using the PSTree Module on the Gallery so you can get the size of a folder, like the one that will be mentioned in PSContentPath after this PR is merged, and everything in it recursively easily as currently this is not built in nicely to .NET from what I can tell and this is why we don't return this in Get-ChildItem natively.

An example returning the User's PSModulePath directories recursive size using the PSTree Module would be pstree -Directory ($env:PSModulePath.Split(';').where{$_ -match 'users'}) -RecursiveSize | select -First 1

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 21 out of 21 changed files in this pull request and generated 14 comments.

Comment thread src/System.Management.Automation/engine/Configuration/PSContentCommands.cs Outdated
Comment thread test/powershell/engine/PSContentPath.Tests.ps1 Outdated
Comment thread test/powershell/engine/PSContentPath.Tests.ps1 Outdated
Comment thread test/powershell/engine/PSContentPath.Tests.ps1
Comment thread test/xUnit/csharp/test_PSConfiguration.cs Outdated
Comment thread test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 Outdated
Comment thread src/System.Management.Automation/CoreCLR/CorePsPlatform.cs
Comment thread test/powershell/engine/PSContentPath.Tests.ps1
Comment thread test/powershell/engine/Basic/PropertyAccessor.Tests.ps1 Outdated
@microsoft-github-policy-service microsoft-github-policy-service Bot added the Review - Needed The PR is being reviewed label Mar 31, 2026
@microsoft-github-policy-service
Copy link
Copy Markdown
Contributor

This pull request has been automatically marked as Review Needed because it has been there has not been any activity for 7 days.
Maintainer, please provide feedback and/or mark it as Waiting on Author

Justin Chung added 4 commits April 1, 2026 13:32
- Added tests for automatic variable.
- Added tests for -default parameter
- added tests for legacty documents path
-Added test for deduplication of PSModulePath
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CL-Engine Indicates that a PR should be marked as an engine change in the Change Log Review - Needed The PR is being reviewed WG-Cmdlets general cmdlet issues WG-Reviewed A Working Group has reviewed this and made a recommendation

Projects

Status: Reviewed

Development

Successfully merging this pull request may close these issues.

7 participants