Repository navigation
Run a function added as a CommandInfo in its own module scope - #28187
Open
Nova-Logic wants to merge 2 commits into
Open
Nova-Logic wants to merge 2 commits into
Nova-Logic wants to merge 2 commits into
Conversation
|
Azure Pipelines: There may be pipelines that require an authorized user to comment /azp run to run. |
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
The defining session state must not be used when the CommandInfo belongs to another runspace.
1 open finding
What changed in this PR
Updates AddCommand(CommandInfo) so module functions execute in their defining module scope.
Changes:
- Selects the function’s defining session state.
- Adds module-scoped and global-function regression tests.
| File | Description |
|---|---|
LocalPipeline.cs |
Uses the function’s defining session state. |
PSCommand.Tests.ps1 |
Tests module and global function invocation. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.
Author
@microsoft-github-policy-service agree |
`LocalPipeline.CreateCommandProcessBase` created the processor for an `IScriptCommandInfo` with the caller's session state. A module function passed to `PowerShell.AddCommand(CommandInfo)` therefore ran outside its module: `$script:` variables were empty and private helper functions were not found. Command discovery already uses the function's own session state (`ScriptBlock.SessionStateInternal`), so invoking the same function by name worked. Use the function's session state, falling back to the engine session state as before.
A CommandInfo captured in another runspace (for example with [powershell]::Create()) must not run against that runspace's session state: a session state belongs to one ExecutionContext. Use the defining session state only when it belongs to the local runspace, otherwise keep the previous engine session state fallback, and add a cross-runspace test.
Nova-Logic
force-pushed
the
fix-addcommand-commandinfo-module-scope
branch
from
October 13, 2026 09:09
487e9b4 to
83d722a
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

A script function passed to the pipeline as a
CommandInfo(PowerShell.AddCommand(CommandInfo)) runs in the caller's session state instead of its module's.LocalPipeline.CreateCommandProcessBasecreates theCommandProcessorwithLocalRunspace.ExecutionContext.EngineSessionState, while command discovery (CommandDiscovery.CreateCommandProcessorForScript/GetScriptAsCmdletProcessor) usesfunctionInfo.ScriptBlock.SessionStateInternal. As a result, a module function invoked this way cannot see its$script:variables or the module's private functions, whereas the same function invoked by name works.This PR makes
CreateCommandProcessBaseuse the function's own session state, falling back to the engine session state as before.Repro (same result on Windows PowerShell 5.1 and PowerShell 7.6):
PR Context
The Hyper-V module hits this on PowerShell 7 for every change to a clustered VM. After
Add-VMHardDiskDrive,Remove-VMHardDiskDriveand similar cmdlets,Microsoft.HyperV.PowerShell.ClusterUtilities.UpdateClusterVMConfigurationlooks upFailoverClusters\Update-ClusterVirtualMachineConfigurationand runs it withPowerShell.Create(RunspaceMode.CurrentRunspace).AddCommand(commandInfo). On PowerShell 7,FailoverClustersis loaded through the Windows PowerShell compatibility session, so the command is an implicit-remoting proxy function. Its body calls& $script:InvokeCommand ..., and because it runs outside its module,$script:InvokeCommandis$null:The VM change is applied, but the cluster's VM configuration is not refreshed. Invoking the same proxy function by name, or with
AddCommand("FailoverClusters\Update-ClusterVirtualMachineConfiguration"), succeeds. That is the behaviour this PR gives theCommandInfopath.Related, but not changed here: for a module-qualified name,
CommandInvocationIntrinsics.GetCommand(name, CommandTypes.Cmdlet)also returns functions. The module auto-loading branch (CommandDiscovery.TryModuleAutoLoading) does not apply theCommandTypesfilter, which is why the Hyper-V module receives the proxy function in the first place. This has been the behaviour since Windows PowerShell, and callers such as the Hyper-V module now depend on it: with the filter applied they would skip the cluster refresh with only a warning.PR Checklist
.h,.cpp,.cs,.ps1and.psm1files have the correct copyright headerTests added to
test/powershell/engine/Api/PSCommand.Tests.ps1:CommandInfosees its$script:variables and private functions. It fails on 7.5.4 with "The term 'Get-PrivateValue' is not recognized" and passes with this change;CommandInfokeeps working (no change in behaviour).