PowerShell Command Naming: Approved Verbs, Clear Nouns, and Better APIs
The difference between a PowerShell command that feels native and one that feels bolted on is often not the implementation. It's the name. In PowerShell, names are part of the interface contract: they drive discoverability, shape user expectations, and determine whether your module behaves like a first-class citizen or like a private script someone published by accident.
Official References
Start with the official documentation for approved verbs, Get-Verb, and the PSScriptAnalyzer rule UseApprovedVerbs.
If you maintain PowerShell modules long enough, you start to see the same mistakes over and over:
- Functions named with verbs PowerShell doesn't recognize
- Plural nouns where PowerShell expects singular object names
- Verb choices that imply one behavior while the function performs another
- "Do everything" verbs like
Invokeused as a substitute for design - State-changing commands that don't support
-WhatIfand-Confirm
This post is about avoiding those mistakes and building command names that feel idiomatic, predictable, and maintainable.
A bad command name spreads farther than you might expect. It shows up in documentation, examples, tab completion, issue reports, code review comments, CI logs, and muscle memory. Once users learn the wrong name, you either carry it forward forever or you break them later when you fix it.
PowerShell isn't just a shell. It's a command discovery environment. Users don't need to remember your entire module if your names fit the ecosystem. They can find commands through patterns:
Get-Command -Verb Get
Get-Command -Module My.Module
Get-Command -Noun VcfCluster
Get-Help Get-VcfCluster -Full
Get-Verb
That only works well when command names follow the conventions the engine, the help system, and the user all expect.
The best PowerShell command names are easy to guess. If I know your module has a cluster object, I should be able to guess that retrieval is probably Get-Thing, creation is probably New-Thing, mutation is probably Set-Thing, deletion is probably Remove-Thing, and validation is probably Test-Thing. If your module breaks that expectation, you increase the amount of documentation the user must read before they can do anything useful.
Names Become Breaking Changes¶
In a published module, a command name is part of the public API. Renaming a command isn't a cosmetic refactor. It's a breaking change for:
- scripts that call the command directly
- internal automation that imports the module non-interactively
- examples in docs, runbooks, and blog posts
- issue comments and support answers people paste back into terminals
- users who rely on tab completion and habit
That's why it's worth being deliberate early. Spending an extra ten minutes choosing between Set, Update, and Invoke is much cheaper than carrying the wrong choice for the next three years because people already depend on it.
If you must rename a published command, a temporary alias can buy time without pretending the old name was right. Export the old name as an alias to the new command so existing scripts that call Fetch-VcfCluster keep working while you ship Get-VcfCluster:
# In the module manifest or .psm1, after defining Get-VcfCluster:
Export-ModuleMember -Function Get-VcfCluster -Alias Fetch-VcfCluster
That's a compatibility shim, not a naming strategy. Aliases don't show up in Get-Help the same way a real command does, they're easy to miss in docs and code review, and they train the same bad habit you're trying to fix. Mark the alias deprecated in help, document the replacement in your changelog, and remove it in a major version once callers have had time to migrate.
Why I Care About This So Much¶
I learned these lessons by writing and maintaining PowerShell modules for infrastructure and private cloud environments, where command names have to hold up in real customer usage, documentation, issue trackers, tab completion, and long-term module evolution. That work included open-source modules which are listed in the Open Source Projects section of my resume.
These were modules customers installed, scripted against, and depended on. At that scale, naming consistency isn't just an aesthetic preference. It's part of whether the module is navigable at all.
That matters because naming isn't cosmetic. In a mature module, names affect:
- Whether users can discover the command
- Whether reviewers can infer intent from a call site
- Whether help content reads naturally
- Whether future maintainers can extend the module coherently
The Verb-Noun Contract¶
PowerShell command names follow a verb-noun pattern:
Both halves matter.
The Verb Declares Intent¶
The verb tells the user what kind of action the command performs. It isn't just a word choice. It's a behavioral promise.
Examples:
Get-Userimplies retrieval without mutationSet-Userimplies modification of existing stateNew-Userimplies creation of new stateRemove-Userimplies deletionTest-Connectionimplies a boolean or validation-style outcome
When the verb and behavior don't match, the command becomes harder to trust.
The Noun Declares the Domain Object¶
The noun should identify the thing the command operates on, using PascalCase and a singular object name:
Get-ClusterSet-ApiTokenRemove-BackupJob
In practice, module authors often use a module-specific prefix to avoid collisions and make related commands group naturally:
Get-VcfClusterSet-VcfClusterRemove-VcfCluster
That's usually a better choice for public modules than generic nouns like Get-Cluster, which can collide conceptually or literally with commands from other modules.
Use Approved Verbs¶
PowerShell has an approved verb list for a reason. The ecosystem depends on a shared action vocabulary. When you use verbs from that list, you get consistency with built-in cmdlets, predictable discoverability, and cleaner analyzer output.
You can inspect the list directly:
Or by category:
Good Choices¶
These verbs usually mean what users expect them to mean:
GetSetNewRemoveAddClearStartStopRestartEnableDisableTestImportExportConvertToConvertFrom
Approved doesn't automatically mean correct for every scenario. A verb can exist in the official list and still be the wrong fit for the command you're designing. For example, Read is appropriate for reading from a source such as user input, but most object retrieval commands in modules should still use Get. Likewise, Update has legitimate uses when a command refreshes or synchronizes state, but many "change this object's settings" commands are better expressed as Set. Sync is approved when alignment between resources is the primary job; don't reach for it when Get, Set, or Update would describe the behavior more clearly.
Bad Choices¶
These names are common in scripts and bad in published modules:
Fetch-UserCreate-VMDelete-SnapshotLaunch-TaskExecute-WorkflowReload-Inventory
The problem isn't that those words are incomprehensible. The problem is that they're outside the standard vocabulary PowerShell users search for.
A Practical Refactor¶
| Not Great | Better | Why |
|---|---|---|
Fetch-VcfCluster | Get-VcfCluster | Retrieval should use Get |
Create-VcfCluster | New-VcfCluster | Creation should use New |
Delete-VcfCluster | Remove-VcfCluster | Removal should use Remove |
Execute-InventorySync | Start-InventorySync or Invoke-InventorySync | Choose the verb that matches the actual behavior. |
Check-VcfUpgrade | Test-VcfUpgrade | Validation should use Test |
When to Use Invoke¶
One of the most abused verbs in PowerShell is Invoke. Authors reach for it when they're not sure what the command should be called, or when the command does something complex and they don't want to think through the actual behavior.
Use Invoke when the command truly means "perform this operation" and no more specific approved verb describes it well. Good cases include:
- Executing a remote request.
- Triggering a workflow or action.
- Running a task that is inherently procedural rather than CRUD-oriented.
Bad cases include:
Invoke-Userinstead ofGet-UserInvoke-BackupPolicyinstead ofSet-BackupPolicyInvoke-ClusterCreationinstead ofNew-Cluster
If the command creates, retrieves, updates, removes, tests, imports, exports, starts, or stops something, use the more specific verb.
Prefer Singular Nouns¶
PowerShell command nouns are normally singular, even when a command can return multiple objects.
Use:
Get-VMGet-ClusterGet-VcfCredential
Avoid:
Get-VMsGet-ClustersGet-Credentials
Why? Because the noun describes the object type, not the cardinality of the result set. A command called Get-VcfCluster can still return ten cluster objects. The singular noun keeps the command aligned with PowerShell's object-oriented mental model.
Name the Object, Not the UI¶
A good noun identifies the underlying domain object, not the screen, wizard, or workflow step where someone first encountered it.
Prefer:
Get-LicenseAssignmentSet-NetworkPoolTest-UpgradePrerequisite
Avoid:
Get-LicensePageSet-NetworkingScreenTest-PrecheckWizard
UI names drift. Object names tend to survive longer.
Avoid Hiding Three Commands Inside One Name¶
Bad command names often come from functions that are trying to do too much. When a function validates input, creates a resource, starts a workflow, polls status, and prints a summary, authors reach for vague names because no single precise verb seems to fit.
That's usually a signal that the command boundary needs work.
For example, a command called Invoke-VcfClusterWorkflow might:
- Validate a configuration
- Create the cluster
- Poll a task
- Emit a summary object
In many modules, that would be easier to understand and easier to name as separate commands:
Test-VcfClusterSpecNew-VcfClusterGet-VcfTask
Sometimes the best naming fix is a design fix.
Make Families of Commands Obvious¶
One of the best signs that a module is well designed is that related commands form natural families:
This matters for both humans and tools:
Get-Command -Noun VcfClusterproduces a coherent set- Documentation reads naturally
- Reviewers can infer lifecycle operations quickly
- Future maintainers know how to name the next command
If your module has Get-VcfCluster, Create-VcfCluster, Update-VcfCluster, and DeleteCluster, you have already introduced naming debt.
Naming and ShouldProcess¶
Verb choice should line up with runtime behavior. If the command changes state, make it an advanced function and support ShouldProcess.
This is the idiomatic pattern:
function Remove-VcfCluster {
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
param(
[Parameter(Mandatory)]
[string]$Name
)
if ($PSCmdlet.ShouldProcess($Name, 'Remove cluster')) {
# Destructive operation...
}
}
Now the verb, the help, and the runtime semantics all agree:
If your command is named Get-* but mutates state, or named Remove-* without supporting -WhatIf, users won't trust it for long.
Advanced Functions Should Still Feel Like Cmdlets¶
A PowerShell advanced function should be named and shaped like a compiled cmdlet, even if it's just a function in a .psm1 file.
That means:
- Use
[CmdletBinding()] - Choose an approved verb
- Use a singular PascalCase noun
- Support pipeline input where it's natural
- Support
ShouldProcessfor state-changing operations - Provide help content that matches the verb's promise
Example:
function Set-VcfCredential {
[CmdletBinding(SupportsShouldProcess)]
param(
[Parameter(Mandatory, ValueFromPipelineByPropertyName)]
[string]$Name,
[Parameter(Mandatory)]
[pscredential]$Credential
)
process {
if ($PSCmdlet.ShouldProcess($Name, 'Update credential')) {
# Update operation...
}
}
}
Aliases Are a Convenience Layer, Not the API¶
Aliases are fine for interactive convenience or a short-term rename bridge (see Names Become Breaking Changes). They aren't a substitute for good cmdlet names.
If the real command is well named, an alias can be helpful in an interactive session:
In a published module, prefer Export-ModuleMember -Alias over asking users to run Set-Alias themselves. The public interface should still be the full cmdlet name. Modules should optimize first for readable scripts, discoverable commands, and help content that reads naturally. Short aliases belong on top of good naming, not instead of it.
Use PSScriptAnalyzer to Catch Bad Verb Choices¶
If you're publishing a module, don't rely on human review alone. Let tooling enforce the baseline conventions.
PSScriptAnalyzer includes the UseApprovedVerbs rule:
A simple CI gate is often enough:
If your module ships commands named Fetch-*, Delete-*, or Create-*, that should be caught before publication, not after users start scripting against it.
Practical Naming Heuristics¶
When I am naming a new PowerShell command, I usually walk through these questions:
- Is this command retrieving, creating, updating, deleting, validating, importing, exporting, starting, or stopping something?
- Which approved verb most precisely matches that behavior?
- What is the actual domain object the command operates on?
- Is the noun singular, PascalCase, and scoped well for the module?
- Will related commands form a clean family around the same noun?
- If the command changes state, does it support
ShouldProcess?
If I can't answer those questions cleanly, the naming problem is usually exposing a design problem upstream.
Opinionated Recommendations¶
If you want your PowerShell module to feel native, do these things consistently:
- Use approved verbs and check them with
Get-Verb. - Use singular PascalCase nouns.
- Keep noun families consistent across the module.
- Avoid
Invokeunless it's genuinely the most accurate verb. - Align destructive or mutating verbs with
SupportsShouldProcess. - Treat PSScriptAnalyzer as part of the module contract, not as an optional cleanup step.
- Remember that a published command name becomes part of your compatibility story.
The goal isn't only stylistic correctness. The goal is a module whose commands are easy to guess, easy to discover, and easy to trust.
References¶
Approved Verb Patterns¶
| Intent | Prefer | Not Great Examples | Notes |
|---|---|---|---|
| Retrieve Data | Get | Fetch, Pull | Use for non-mutating retrieval of objects |
| Create New Resource | New | Create, Make | Use when new state is created |
| Modify Existing Resource | Set | Change, Configure | Best when changing existing state |
| Remove Resource | Remove | Delete, Destroy | Pair with ShouldProcess |
| Validate or Probe | Test | Check, Verify | Good for health checks and prerequisites |
| Trigger an Operation | Invoke or Start | Execute, Run, Launch | Choose carefully based on the actual action |
| Enable Feature | Enable | TurnOn | Use for toggling on |
| Disable Feature | Disable | TurnOff | Use for toggling off |
| Refresh or Synchronize | Sync or Update when accurate | Refresh, Reload | Sync is approved; use it when alignment is the primary job |
Good and Bad Examples¶
| Not Great | Better | Why |
|---|---|---|
Fetch-NsxPolicy | Get-NsxPolicy | Retrieval should use Get |
Create-VraProject | New-VraProject | Creation should use New |
Delete-VcfPassword | Remove-VcfPassword | Removal should use Remove |
Check-VsphereUpgrade | Test-VsphereUpgrade | Validation should use Test |
Get-Clusters | Get-Cluster | Nouns should be singular |
Invoke-VcenterCreation | New-Vcenter | The command creates a resource, so say so directly |
Useful Commands¶
| Command | Purpose |
|---|---|
Get-Verb | Sort-Object Verb | List approved verbs |
Get-Command -Module My.Module | Review the exported command surface |
Get-Command -Noun VcfCluster | Inspect noun families |
Get-Help Get-VcfCluster -Full | Check whether help matches the cmdlet contract |
Invoke-ScriptAnalyzer -Path . -Recurse -EnableExit | Lint the module in CI |
Invoke-ScriptAnalyzer -Path .\src -IncludeRule UseApprovedVerbs | Check for verb naming problems specifically |
Naming Checklist¶
| Question | Yes/No Test |
|---|---|
| Is the verb approved? | Check with Get-Verb or PSScriptAnalyzer |
| Is the noun singular? | Prefer Cluster, not Clusters |
| Is the noun module-scoped enough? | Prefer VcfCluster over generic Cluster in a public module |
| Does the verb match the behavior? | Get should not mutate, Remove should remove |
| Does the command family stay coherent? | Related lifecycle operations should share the same noun |
Does a state-changing command support ShouldProcess? | Use [CmdletBinding(SupportsShouldProcess)] |
Disclaimer
This is not an official VMware by Broadcom document. This is a personal blog post.
The information is provided as-is with no warranties and confers no rights.
Please, refer to official documentation for the most up-to-date information.