PowerShell: Wrong module is auto-loaded when using unqualified cmdlet and multiple modules contain a cmdlet by that name
Prerequisites
- Write a descriptive title.
- Make sure you are able to repro it on the latest released version
- Search the existing issues.
- Refer to the FAQ.
- Refer to Differences between Windows PowerShell 5.1 and PowerShell.
Steps to reproduce
- Install the VMWare PowerCLI module. Available in the PowerShell gallery at: https://www.powershellgallery.com/packages/VMware.PowerCLI
- Open a new PowerShell instance with standard settings (including, but not limited to,
PSModuleAutoloadingPreference
being undefined). [ Tip: Use-NoProfile
if your profile load any module or modifies any settings. ] - Run
Get-VM
.
Expected behavior
> Get-VM
Name State CPUUsage(%) MemoryAssigned(M) Uptime Status Version
---- ----- ----------- ----------------- ------ ------ -------
Ubuntu Off 0 0 00:00:00 Operating normally 11.0
Ubuntu 22.04 LTS Off 0 0 00:00:00 Operating normally 11.0
Windows 10 Enterprise 21H1 Off 0 0 00:00:00 Operating normally 10.0
.......
Actual behavior
> Get-VM
WARNING: Please consider joining the VMware Customer Experience Improvement Program, so you can help us make PowerCLI a better product. You can join using the following command:
Set-PowerCLIConfiguration -Scope User -ParticipateInCEIP $true
VMware's Customer Experience Improvement Program ("CEIP") provides VMware with information that enables VMware to improve its products and services, to fix problems, and to advise you on how best to deploy and use our products. As part of the CEIP, VMware collects technical information about your organization’s use of VMware products and services on a regular basis in association with your organization’s VMware license key(s). This information does not personally identify any individual.
For more details: type "help about_ceip" to see the related help article.
To disable this warning and set your preference use the following command and restart PowerShell:
Set-PowerCLIConfiguration -Scope User -ParticipateInCEIP $true or $false.
Get-VM: 15/05/2023 17:49:56 Get-VM You are not currently connected to any servers. Please connect first using a Connect cmdlet.
Error details
> Get-Error
Exception :
Type : VMware.VimAutomation.Sdk.Types.V1.ErrorHandling.VimException.ViServerConnectionException
ErrorId : Core_BaseCmdlet_NotConnectedError
ErrorCategory : ResourceUnavailable
RecommendedAction : Connect to server.
Severity : Error
TargetSite :
Name : ThrowNotConnectedError
DeclaringType : VMware.VimAutomation.Sdk.Util10Ps.BaseCmdlet.MessageHelper
MemberType : Method
Module : VMware.VimAutomation.Sdk.Util10Ps.dll
Message : 15/05/2023 17:49:56 Get-VM You are not currently connected to any servers. Please connect
first using a Connect cmdlet.
Data : System.Collections.ListDictionaryInternal
Source : VMware.VimAutomation.Sdk.Util10Ps
HResult : -2146232832
StackTrace :
at VMware.VimAutomation.Sdk.Util10Ps.BaseCmdlet.MessageHelper.ThrowNotConnectedError()
at VMware.VimAutomation.Sdk.Util10Ps.BaseCmdlet.ConnectionDispatchHelper.DispatchObjectsHelper(IList`1
connectionList, IList`1 moListFromCmdletParameter, Dictionary`2 runlist, Dictionary`2 connectionIdToUidBourneIdList,
Boolean isConnectionListExplicit)
at VMware.VimAutomation.Sdk.Util10Ps.BaseCmdlet.ConnectionDispatchHelper.DispatchObjects(ParallelConnectionExecutor
executor, UidDispatchHelper uidDispatchHelper, IList`1 connectionList, Boolean isConnectionListExplicit, IList`1
moListFromCmdletParameter, String moListFromCmdletParameterName, Boolean isServerParameterObnOnly)
at VMware.VimAutomation.Sdk.Util10Ps.BaseCmdlet.BaseCmdlet.ProcessRecordErrorHandled()
at VMware.VimAutomation.ViCore.Util10Ps.BaseCmdlet.BaseCmdlet.ProcessRecordErrorHandled()
CategoryInfo : ResourceUnavailable: (:) [Get-VM], ViServerConnectionException
FullyQualifiedErrorId : Core_BaseCmdlet_NotConnectedError,VMware.VimAutomation.ViCore.Cmdlets.Commands.GetVM
InvocationInfo :
MyCommand : Get-VM
ScriptLineNumber : 1
OffsetInLine : 1
HistoryId : 2
Line : Get-VM
PositionMessage : At line:1 char:1
+ Get-VM
+ ~~~~~~
InvocationName : Get-VM
CommandOrigin : Internal
ScriptStackTrace : at <ScriptBlock>, <No file>: line 1
Environment data
> $PSVersionTable
Name Value
---- -----
PSVersion 7.3.4
PSEdition Core
GitCommitId 7.3.4
OS Microsoft Windows 10.0.22621
Platform Win32NT
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
WSManStackVersion 3.0
Visuals
No response
Additional information
This is not the same issue as a function taking precedence over a cmdlet as documented. In this case both names refer to a cmdlet:
> Get-Command -Name "Get-VM" -All | ft -AutoSize
CommandType Name Version Source
----------- ---- ------- ------
Cmdlet Get-VM 2.0.0.0 Hyper-V
Cmdlet Get-VM 13.0.0.20797821 VMware.VimAutomation.Core
PowerShell decided to use VMware.VimAutomation.Core\Get-VM
over Hyper-V\Get-VM
contrary to reasonable exepctations. Perhaps because [Version]"13.0.0.20797821" -gt [Version]"2.0.0.0"
or maybe because "VMware.VimAutomation.Core" -gt "Hyper-V"
.
I couldn’t find any documentation saying how the module auto-load feature assigns priorities in such a case so I don’t know if there even is a rule and it’s not arbitrary. Either way - assigning arbitrary priorities is unexpected and assigning the observed priorities is unexpected.
Perhaps it’s difficult to set a rule that would do the expected thing in all use cases. Allowing the user to set precedence of module load is probably the best approach.
About this issue
- Original URL
- State: closed
- Created a year ago
- Comments: 25 (11 by maintainers)
$Env:PSModulePath says where to search for commands. By default the order is
Someone I worked with used to say “What the software does is not random, you just can’t see the pattern” . It is working from most specific definition of the command (user only) to most general (installed with the OS).
Yes, and that is by design - although because they are in the utility module, that will often be loaded by something else. But if you take something like
start-transcript
you can replace it with your ownstart transcript
command if you so choose. There have been comments in the past that if users can be tricked into installing a malicious module, then it can replace common commands with harmful ones (true) but the ability to replace a command is a key one.I think the enhancement I describe would still be useful for module authors that want to ensure their commands are module qualified without having to import modules with
-Force
or having to create aliases or module qualifying all commands since the module author doesn’t know what commands are available on the system or loaded.They’re scoped the same as any other session state item. So as long as the second script is not dot sourced, it can follow the same pattern of setting an alias at the top.
Set-Alias
also has a-Scope
parameter you can specifyscript
to if desired.Wouldn’t
Import-Module Hyper-V
solve this? I guess if they’re both imported it can be problematic for specific commands but you can always do${alias:Get-VM} = 'Hyper-V/Get-VM'
@kilasuit No, it does not but usually is good enough most of the time. As you mention and has been stated by others the only way is to use module qualified or by using prefixes.
There is one enhancement that could be implemented as brought up by the OP in this issue. The use of module qualified command names is tedious and not very pretty in scripts. What could be done is having a way in scripts to define that I’m using commands from this module as if they were module qualified.
How I see this working is as follows:
using -commands Hyper-V
is defined (note I don’t like naming things or don’t thinkusing
orcommands
is the best option just an example)Hyper-V
and then any commands in the script from that module would be treated as module qualified.