Increasing visibility of cmdlet design guidelines


In PowerShell v1, we published a set of cmdlet design guidelines that included direction on how to properly name cmdlets. Throughout the v2 cycle, we’ve seen an increase in cmdlet authors deviating from the standards in unexpected ways. Our user community (you all) has become particularly vocal in recent months that we needed to do something in v2 to curb this trend.

This led to a series of discussions on our team about an appropriate course of action for v2. The outcome of those discussions includes changes to Import-Module where we will perform name checking on imported commands. We decided not to disallow unapproved verbs or characters (i.e. the import succeeds), but we will display a warning on import of commands that use unapproved verbs or characters as a user-visible speed bump to more strongly encourage the right behavior.

If you think you might be using unapproved verbs or characters in your commands, please see the Additional Details section below.


Additional Details

What’s the problem?

As described in the summary above, we’ve taken some action recently to address the issue of cmdlet authors departing from the design guidelines, which appears to be increasing in frequency and severity lately. We’ve had many in-depth discussions on the PowerShell team over the past several weeks about how to address this problem and what might be feasible in v2 given our schedule for Windows 7. We believe we have some improved guidance and mitigation that will help curb departures from the guidelines, bubble up the need to adhere to the prescribed naming format, and provide a clear path from v1 to v2 and beyond.


How have the guidelines changed in PowerShell v2?

First, it’s helpful to look at what we’ve done in v2 as the basis for determining what the guidance should look like, both for those implementing on v1 only as well as those targeting v2 or looking to move eventually to v2. In v2, we’re using modules as the primary mechanism to address the issues of namespaces, code isolation, and naming collisions, so this is where we’ve targeted some changes. With modules, we’ve provided a few options as described in the following guidelines:

  1. Name your commands using approved verbs only. While every technology has its own domain-specific language that users of that technology understand and are comfortable with, these terms have different meanings or are non-existent when used outside that technology. This is a key stumbling block that prevents reuse of knowledge and skills across technology boundaries. As a broad automation and management platform that spans multiple technologies, PowerShell helps break down these knowledge silos with a standard set of command verbs that are used across all technologies and domains.
  2. Structure your commands as verb-noun pairs. We have built-in facilities like Get-Command -Verb and Get-Command -Noun which make it easier to explore the system, group related commands, and find the command you’re looking for to complete the task without having to always resort to documentation or have an in-depth knowledge of the technology. We also have many 3rd party utilities that depend on commands being formatted as verb-noun pairs and using only approved verbs.
  3. Prefix your nouns with a short, unique technology-specific moniker. This should be an alphanumeric moniker that doesn’t include extra characters or symbols like colons or slashes. In PowerShell, where we don’t want our built-in commands to clash with other commands, we use the prefix PS. The Active Directory team prefixes all their command nouns with ‘AD’, e.g. Get-ADUser. This makes it easy to say Get-Command -Noun AD* to find all the AD cmdlets.
  4. Use a snap-in or module qualifier where name collisions exist. PowerShell has had the ability since v1 to reference a command using a fully qualified name. In v1, this is done by specifying the snap-in that contains the command followed by a backslash followed by the command name. For example:

    In v2, you can also use the module name as the qualifier. For example:


  5. You can use the qualifiers described above to ensure that scripts are always using the correct command. In v1 scripts, you can use explicit naming with snap-in qualifiers. When migrating those scripts to v2, if the module name matches the snap-in name, no changes are required. With modules in v2, where imported names collide with existing names, the last imported name overwrites or shadows the existing name. So you can alternatively add a call to Import-Module at the top of the script and use implicit, non-qualified names. You can still access the shadowed name using the fully qualified syntax, if necessary.
  6. Many times, snap-in names are long and unwieldy to type. However, with modules in v2, you can use a module manifest to abstract long, ugly assembly names with a short, pithy module name. “BitsTransfer” is much easier to type than “Microsoft.BackgroundIntelligentTransferService.Management.Interop” as a module qualifier.
  7. As consumers of a module, users are given additional control with the Prefix parameter on Import-Module which allows custom prefixing on imported commands.


Why change things now?

With v1, we took the approach of exposing the rules above strictly as design guidelines with no enforcement mechanism or other restrictions in the PowerShell engine itself. However, we’re concerned that with rapidly accelerating adoption in v2, we’re going to see a proliferation of people deviating from the guidelines, whether through lack of knowledge of the guidelines or a different view about what the guidelines should be or refusal to adhere to the guidelines for one reason or another. We’ve spent a considerable amount of time debating what level checking to do and how violations should get exposed to the user. With the introduction of modules in v2, we have a one-time opportunity to perform at least some level of verification on imported commands against the guidelines.


What does the new name checking behavior look like?

Here’s a detailed description of the name validation behavior we’re adding to Import-Module:

  1. Import-Module evaluates imported commands according to the following guidelines:
    • Hyphenated command names can have exactly one hyphen where the hyphen symbol as typed using a single key on the keyboard is used, not figure-dash, en-dash, em-dash, horizontal bar, or other types of dashes.
    • Where a command name includes a hyphen, the characters preceding the hyphen constitute the verb and characters following the hyphen constitute the noun.
    • Hyphenated commands can only use approved verbs.
    • Hyphenated command names cannot include characters prohibited by the cmdlet design guidelines: # , ( ) { } [ ] & – / \ $ ^ ; : ” ‘ < > | ? @ ` * % + = ~
    • Only hyphenated function and cmdlet names are evaluated against the naming guidelines; alias, variables, and non-hyphenated functions or cmdlets are not.
    • Commands are evaluated against the guidelines irrespective of module type (manifest, script, or binary). However, commands from dynamic modules created with New-Module are not checked.
  1. If Import-Module encounters one or more commands that violate the guidelines, the command is still imported but warnings are displayed.
    • For non-approved verbs, the following warning is displayed:
Some imported command names include unapproved verbs which might make them less discoverable. Use the Verbose parameter for more detail or type Get-Verb to see the list of approved verbs.

    • For restricted characters, the following warning is displayed:

Some imported command names contain one or more of the following restricted characters: # , ( ) { } [ ] & – / \ $ ^ ; : ” ‘ < > | ? @ ` * % + = ~

  1. Warnings are only displayed for modules that are imported using Import-Module or the PowerShell API (warnings cannot be suppressed when using the API).
  2. Using the Verbose switch on Import-Module provides additional detail on command naming violations:
    • Displays which commands are using restricted characters.

The command name ‘<command>’ contains one or more of the following restricted characters: # , ( ) { } [ ] & – / \ $ ^ ; : ” ‘ < > | ? @ ` * % + = ~

    • Displays which commands are using non-approved verbs.
    • For non-approved verbs that have one or more recommended alternative verbs, the recommended approved verbs are also displayed as follows:

The command name ‘<command>’ includes an unapproved verb which might make it less discoverable. The suggested alternative verbs are “<approved verb>”, “<approved verb>”, “<approved verb>”.

  1. Users can suppress warnings about command names by specifying the DisableNameChecking switch on Import-Module.
  2. The Get-Verb function can be used to obtain the list of approved verbs.


What about commands that have already shipped?

We realize that some products might already have shipped with commands that don’t adhere to the guidelines. Our recommendation is that as these products move forward in future releases, the command names should be changed to adhere to the guidelines. To avoid breaking customer scripts written using the old commands, we recommend creating aliases for the old commands that map to the new names. Since we don’t perform name checking on aliases, no warnings will be displayed for these aliases as long as the underlying function/cmdlet has been changed to use approved naming conventions.

Dan Harman
Program Manager
Windows PowerShell