Add PSModulePath override to InitialSessionState.#27126
Add PSModulePath override to InitialSessionState.#27126adamdriscoll wants to merge 6 commits intoPowerShell:masterfrom
Conversation
6010e41 to
a8d9b15
Compare
There was a problem hiding this comment.
Pull request overview
Adds an opt-in InitialSessionState.PSModulePath override so hosts can control module discovery per runspace instead of relying on the process-wide PSModulePath environment variable.
Changes:
- Introduces
InitialSessionState.PSModulePathand ensures it’s copied onInitialSessionState.Clone(). - Routes module-path resolution through the new per-runspace override and avoids AppDomain-level module path caching when the override is set.
- Adds xUnit coverage for runspaces/runspace pools and verifies the override doesn’t leak via the AppDomain module cache.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
src/System.Management.Automation/engine/InitialSessionState.cs |
Adds PSModulePath property and clones it. |
src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs |
Reads module path from InitialSessionState.PSModulePath when provided. |
src/System.Management.Automation/engine/Modules/ModuleCmdletBase.cs |
Disables AppDomain module cache writes when a custom module path override is active. |
src/System.Management.Automation/engine/Modules/ImportModuleCommand.cs |
Disables AppDomain module cache reads when a custom module path override is active. |
test/xUnit/csharp/test_Runspace.cs |
Adds tests for custom per-runspace module path behavior and cache leakage prevention. |
|
Honestly, this is great enchantment to the Apps that are using SDK and are distributed along with all necessary files. Good work! |
| } | ||
|
|
||
| if (includeSystemModulePath) | ||
| if (includeSystemModulePath && !HasCustomPSModulePath(context)) |
There was a problem hiding this comment.
When PSModulePath is set, this line suppresses $PSHOME/Modules even when the caller passes includeSystemModulePath: true.
This means hosts that set a custom path lose access to core built-in modules unless they manually include $PSHOME/Modules in their custom string.
Is this the intended behavior? If so, the XML doc on InitialSessionState.PSModulePath should call this out explicitly. If not, consider letting includeSystemModulePath still take effect.
There was a problem hiding this comment.
I'll look at the value a little more clearly. I don't know how to set that on InitialSessionState so I want to make sure I understand that I can, if I need to, exclude $PSHome/Modules from the PSModulePath.
|
@adamdriscoll Great idea! Declarative control over created runspaces could reduce some confusion over PSModulePath inheritance. Just a few things to consider.
|
|
|
||
| internal static bool HasCustomPSModulePath(ExecutionContext context) | ||
| { | ||
| return context?.InitialSessionState?.PSModulePath != null; |
There was a problem hiding this comment.
This allows empty strings. Is that intended?
There was a problem hiding this comment.
I'd like that. It would be nice to set it to an empty value to bypass any module look up. I don't know exactly why I would do that but I could see it being useful.
src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs
Outdated
Show resolved
Hide resolved
|
$env:PSModulePath inside the runspace will still reflect the process-level env var, not the custom path. Get-Module -ListAvailable will enumerate from the custom path though. Should the env var be updated in-session to match? I considered this but I guess I didn't know how to proceed. I feel like if I set it in-session, it wouldn't really be a true environment variable. We might be able to update the environment variable provider to pull the runspace-scoped value. I also don't want to actually update the environment variable for the whole process. I guess it all depends on how much calling scripts may examine or use this variable. In PowerShell Universal, I don't know if it would matter much if we used the true value here. Could you add a test for Clone() preserving PSModulePath? Something like: set it on the original, clone, assert equality, then verify independence. Will do. The cache-leak test covers custom→default direction. Worth adding the reverse: load a module in a default runspace, then verify a custom-path runspace doesn't pick it up from the AppDomain cache. Will do. Could you add a test asserting that system modules from $PSHOME/Modules are not available when a custom PSModulePath is set? Will do. I should have a little time this afternoon to get these changes in. |
|
Private |
PR Summary
Adds PSModulePath override to InitialSessionState so we can rely on per-runspace configuration rather than process level environment variable.
PR Context
When hosting the PowerShell SDK in a multi-runspace environment, dealing with
$ENV:PSModulePathis difficult. It inherits from many places (e.g. system, user, automatic values, parent processes) and changes based on version (PS7 and WinPS). It will be changing again once thePSContentPathPR is merged. As a PowerShell host process developer, I want to have full control over where the PowerShell SDK looks for modules, even system ones. This change should not affect existing users of eitherpwsh.exeor other users of the PS SDK as the feature is opt-in.The goal of this PR is to avoid environment variables completely and have a small, focused property I can set when constructing a runspace to completely control module loading.
PR Change Summary
I have added a new
PSModulePathproperty toInitialSessionState. In several key areas, I check for, and use, that property to ensure myPSModulePathis exactly as I expect. I have added some tests and verified they pass. I have run the code interactively to verify my change.I will admit that it has been many years since I have contributed so I may be missing something.
Output from the preview
pwsh.exeOutput from a custom runspace in preview
pwsh.exePR AI Usage
I have used AI (Copilot\OpenAI GPT5.4) to assist in the development of the code for this but have kept it to a small, focused changes and reviewed it extensively. The contents of this description are my own words and typos.
PR Checklist
.h,.cpp,.cs,.ps1and.psm1files have the correct copyright header