PowerShellPracticeAndStyle
PowerShellPracticeAndStyle copied to clipboard
Command Prefixes
Should we be embedding Command Prefixes in function names, or using the PSD1 to specify the default?
I'm going to start this debate by saying that I think putting prefixes into the code is wrong.
- You break the -Prefix feature on Import-Module (nobody wants two prefixes)
- You break discoverability, because nobody can Get-Command -Noun <GenericNoun> and find your commands (because your noun is "ADUser" instead of "User" and "ADGroup" instead of "Group").
The counter argument seems to be that "ADUser" and "ADGroup" are actually nouns, not just nouns with a module prefix, and I guess I would be OK with that -- but that should be the distinction you make, and the bar you set for hardcoding a prefix: the prefixed noun should be so obvious that it's discoverable, and that all of your users will intuitively know to search for it. E.g.; "ADUser" might be ok, but "QADUser" isn't :wink:
Some facts (if I am not mistaken, double check would not hurt).
DefaultCommandPrefix in PSD1 manifests was introduced in v3:
New DefaultCommandPrefix key in module manifests that avoids name conflicts without changing module code.
Something was amended in 4.0:
In Windows PowerShell 4.0, if a module uses the DefaultCommandPrefix key in its manifest, or if the user imports a module with the Prefix parameter, the ExportedCommands property of the module shows the commands in the module with the prefix. When you run the commands by using the module-qualified syntax, ModuleName\CommandName, the command names must include the prefix.
Thus, it looks like if I want to support 2.0 or if I want the same behavior for 3.0 and 4.0 then I should not use DefaultCommandPrefix in module manifests. Hardcoded prefixes may be not perfect but they may have less issues.
I have added the word Sec as a prefix to all functions and files that I create for PoshSec. I could go back and remove them and have the module manifest pick them up.
This is mostly right now for v2 compatibility. However, I am probably moving in a direction to move away from V2 support to take advantage of some of the language benefits of later releases.
I think we should use the prefix’s but put a note in for v2 compatibility this might not be wise.
The issues that are identified above are exactly why I recommend very strongly against adopting this style until v4 becomes the community baseline. The bugs/discrepancies between v3/v4 on manifest defined prefixes are such that this feature should be avoided for all modules that will work on v2 or v3 IMHO. Plus, thinking about the large number of script or command authors that won't think about version when they create and share content, I prefer erring on the cautious side, so that their content has a better chance of working in downlevel versions as is, consistent across versions.
All that said, if the vote is against me then my only request is that manifest-defined prefixes is listed as a recommended style specifically for modules that require PSv4+ due to the bugs in v3.
I guess that brings up a good point, should the docs be based on PS versions?
i use to avoid the defined prefix because i had run in to problems with it in V3, but with V5 (using it for psgit) it seems pretty ironed out..
Frankly, I really dislike prefixes. I always have. I would much rather just make people type PoshSec\Get-Noun if they have more than one Get-Noun on their system.
Using "SecNoun" when I mean "Noun from the PoshSec module" breaks about a quarter of the usefulness of nouns, and isn't guaranteed to make it unique anyway, because unlike module names which are unique because they have to go in the file system, and because PowerShellGallery will enforce it :stuck_out_tongue_winking_eye: prefixes don't have any guarantee of uniqueness at all.
For what it's worth, I don't think most people can write code to target PS2 (you have to have PS2 to target it, if you're just testing with PS4 and -Version 2 your chances are very slim).
In any case, PS2 was so long ago that I do not want to take it into account in the style guide, but I do think it's worth at least mentioning in the Best Practice stuff (like this), and even suggesting an alternate best practice for people who want to target it.
I don't even want to talk about prefixes, but since the Microsoft Team has required people to use prefixes, but completely failed to address the issues (in fact, I think they're lying about insisting on the use of prefixes "from the very early days"), I think we have to. They don't even mention that there's a module manifest way of specifying them, or that users can specify their own prefixes at import time.
Bottom line, I think we have to document what @nightroman wrote, and then recommend hard-coding the prefix if you need to support PS2 (or PS3?).
Is there actually any reason the fully-qualified name difference matters? bothers you for PS3 -- not sure why it would, if you're using prefixes it's because you don't expect people to type the full name, right?).
We've got some consultants here right? Have any of you ever seen people using the fully qualified Module\Verb-Noun command syntax?
I have created some modules for our company, for internal use. Other departments are on the edge of creating modules, and I have made a template for internal modules. Why invent a wheel twice?
We have had some nasty problems with duplicate commands, so in the template I use a default prefix. This "guides" others to use the prefix. (Most of them just don't bother for any name convention) The prefix is, by the way, a well known term in our company.
So I was a big fan of prefixes. However, recently we started using the modules more company wide and 'discovered' 180 2008 servers with PowerShell 2 in a dark corner. Old applications, no budget, ....
Now I ended up here, while trying to make the code compatible for PowerShell 2. I do not like the idea of hardcoding the prefix in all functions, but there seems no other way.
At this point not sure if we 'leave the old servers behind', or put a lot of effort in making the code work for PowerShell 2.
@Jaykul: I have never seen someone using Module\Verb-Noun command syntax
But I am not a consultant.
Considering Microsoft is deprecating v2 do we still need to consider v2 in this issue?
Also, when I write a module I expect to be used by a lot of folks I usually module-name prefix commands so that I'm sure I'll get the built-in command instead of someone's poorly written proxy. Seen a few too many bugs due to command proxies.
My feelings about this have changed a bit in the last two years, but frankly, deprecating PS2 (or even PS3) doesn't figure into that. So here's a few random thoughts:
Microsoft hard-codes the prefixes into their command names.
They do this for everything. Even when the module name is, say, "AzureRm" and your prefix is "AzureRm" -- how is Get-AzureRmVm any better than AzureRm\Get-VM? Azure is an extreme example: they have modules like "AzureRm.DevTestLabs" and the prefix ends up being the rather arduous "AzureRmDtl" ...
If you rely on DefaultCommandPrefix it's just a default.
It gets replaced when I specify a -Prefix when I import the module.
Remember that the solution for implicit remoting is to use prefixes.
What is your command going to look like when I do something like:
Import-Module YourModule -PSSession $S1 -Prefix Server1
Import-Module YourModule -PSSession $S2 -Prefix Server2
Prefixes still have no guarantee of uniqueness.
Even if you own the module "AzureRm" and you use "AzureRm" as your prefix, you have no way to prevent someone from colliding with your command name -- the only way to unambiguously be completely sure is to use the fully qualified name, even if that means you have to write AzureRm\Get-AzureRmVm.
That's why the PowerShell Gallery is so worried about command collisions that it won't let you install a module that has command-name collisions without acknowledging it...
But the bottom line is ...
Personally, I still don't like them, and I don't want to recommend prefixing everything.
the only way to unambiguously be completely sure is to use the fully qualified name, even if that means you have to write AzureRm\Get-AzureRmVm
But even then, someone could create aliases and redirect your command invocations (because you can create aliases that look like fully qualified names). Just mentioning that because it comes as a surprise to most folks.
Personally I still recommend prefixing every command. I suspect I won't change that stance unless/until PowerShell provides a way to control the priority used in command name resolution via a using statement (e.g. using modulename; <# For the remainder of this scope, commands from modulename will take priority over other commands of the same name #>} -- something like that anyway).
It's true that basically anything can be stepped on in PowerShell -- if it's deliberate. Even properties and methods:
Update-TypeData -TypeName System.String -MemberName Length -MemberType ScriptProperty -Value { 0 }
A few years later, and this just came up again for me.
I'm watching a video talking about the alpha Microsoft.Graph module that uses AutoRest to generate over 2000 commands in dozens of auto-generated modules. This module bundle highlights one key thing that prefixes have that fully qualified command names don't: the ability to use one constant prefix across many modules. Looking at the most recent updates of the Az module, all cmdlets are prefixed with Az, no matter which module they come from. That model supports faster loading time (only load the modules that are actually required) while maintaining prefix consistency across a very large set of cmdlets that spans many modules.
On the flip-side though, some auto-generated cmdlets have very long names already, so prefixes really should be kept short. Like 2-4 characters short. Otherwise, they're just adding more noise to a very noisy command set. Also, what would you pick for an prefix for commands in Microsoft.Graph? Az is meaningful for Azure, because the commands run against Azure, but Graph (or some shorthand version of Graph) isn't as meaningful because Microsoft.Graph is "the gateway to data and intelligence in Microsoft 365". With that in mind, maybe M365 would be a better noun prefix, or just 365. Then we could have Get-365User, and so on.
Or maybe we should just do away with module names and nouns altogether, and just use emojis since they are supported in modern consoles.
☁️\Get-👤 Get-👥
etc. 🤣
First of all, as far as using module <name> -- that actually works. Given two modules with Get-Number, using module works, the same as Import-Module (but also imports the types), except that Import-Module can be run anywhere (whereas using can only be the first thing in a script).
IpMo AllOnes
Get-Number # calls AllOnes\Get-Number
IpMo AllTwos
Get-Number # calls AllTwos\Get-Number
Ipmo AllOnes
Get-Number # calls AllOnes\Get-Number
AllTwos\Get-Number # calls AllTwos\Get-Number

In my opinion, the Azure modules are a great example of why prefixes are awful.
Although I'm glad it's "Az" in place of "AzureRM" now (AzureRmApplicationInsights was always entirely too much prefix), the "Az" is not the whole prefix. That is to say, for most modules, the entire module name is in the prefix:
- The
Az.Mediamodule commands are all prefixed withAzMedia - The
Az.KeyVaultmodule commands are all prefixed withAzKeyVault - The
Az.NotificationHubsmodule commands are all prefixed withAzNotificationHub - The
Az.AnalysisServicesmodule commands are all prefixed withAzAnalysisServices - The
Az.OperationalInsightsmodule commands are all prefixed withAzOperationalInsights - etc.
On top of that, the Az prefix prefix is the third iteration on the module prefix. We've gone from Get-AzureFoo to Get-AzureRmFoo to Get-AzFoo -- The modules are basically the same. In the most recent change, the actual AzureRm and Az commands are all mostly identical, but we all had to do multiple rewrites just because the team decided to change prefixes.
If you care, I bet I can come up with a rough estimate of how much that name change cost our company, because we had stories in the backlog and tasks. Not cool.
Ultimately, the prefixes aren't applied consistently. A few of the modules, like Az.MachineLearning and Az.DevTestLabs and Az.Compute use a different prefix (AzMl and AzDtl and AzVm respectively) and there are a few commands in some modules (well, at least in the Az.Compute module) that are only prefixed with "Az" ...
Hypothetically, it's good they're shipping a collection of modules rather than a single module, but the prefix has nothing to do with that. Not having a prefix isn't going to force anyone to publish only one module.
In fact, the net result of the prefix is that it leads people down the wrong path: they write scripts calling functions from multiple modules and don't even know which modules they're using, so they don't import them explicitly -- nor document which ones they are using, so you have to install "Az" (all 120 modules worth of it) and load the modules implicitly on-demand, resulting in slower scripts.
using module is disappointing. I was really hoping it would be a better successor to Import-Module, such that I could use it to indicate which modules were a priority over a script block or file.
For example:
nmo -name Test1 {function Get-Thing {'Thing1'}} | ipmo
nmo -name Test2 {function Get-Thing {'Thing2'}} | ipmo
Get-Thing # returns 'Thing2'
using module Test1 {
Get-Thing # I want this to return 'Thing1'
}
Get-Thing # This would return 'Thing2' again
That doesn't work though, because as you point out, using module must be the first thing in the file, like a precompiler directive.
As for all of the rest of the comments you made @Jaykul, while I would prefer a world without prefixes, I don't believe that no prefix is a realistic option given what we have available today as a way to define the module we want to use in scripts/functions, which is amazing given that modules came out in 2009 -- 10 years later, and it's still quite messy.