Skip to content

PowerShell Command Naming: Approved Verbs, Clear Nouns, and Better APIs

PowerShell PowerShell

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 Invoke used as a substitute for design
  • State-changing commands that don't support -WhatIf and -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:

Verb-Noun

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-User implies retrieval without mutation
  • Set-User implies modification of existing state
  • New-User implies creation of new state
  • Remove-User implies deletion
  • Test-Connection implies 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-Cluster
  • Set-ApiToken
  • Remove-BackupJob

In practice, module authors often use a module-specific prefix to avoid collisions and make related commands group naturally:

  • Get-VcfCluster
  • Set-VcfCluster
  • Remove-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:

Get-Verb | Sort-Object Verb

Or by category:

Get-Verb | Group-Object Group | Sort-Object Name

Good Choices

These verbs usually mean what users expect them to mean:

  • Get
  • Set
  • New
  • Remove
  • Add
  • Clear
  • Start
  • Stop
  • Restart
  • Enable
  • Disable
  • Test
  • Import
  • Export
  • ConvertTo
  • ConvertFrom

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-User
  • Create-VM
  • Delete-Snapshot
  • Launch-Task
  • Execute-Workflow
  • Reload-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-User instead of Get-User
  • Invoke-BackupPolicy instead of Set-BackupPolicy
  • Invoke-ClusterCreation instead of New-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-VM
  • Get-Cluster
  • Get-VcfCredential

Avoid:

  • Get-VMs
  • Get-Clusters
  • Get-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-LicenseAssignment
  • Set-NetworkPool
  • Test-UpgradePrerequisite

Avoid:

  • Get-LicensePage
  • Set-NetworkingScreen
  • Test-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-VcfClusterSpec
  • New-VcfCluster
  • Get-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:

Get-VcfCluster
New-VcfCluster
Set-VcfCluster
Remove-VcfCluster
Get-VcfCredential
New-VcfCredential
Set-VcfCredential
Remove-VcfCredential
Test-VcfCredential

This matters for both humans and tools:

  • Get-Command -Noun VcfCluster produces 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:

Remove-VcfCluster -Name sfo-m01 -WhatIf
Remove-VcfCluster -Name sfo-m01 -Confirm

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 ShouldProcess for 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:

Set-Alias -Name gvcfcluster -Value Get-VcfCluster

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:

Invoke-ScriptAnalyzer -Path .\src -IncludeRule UseApprovedVerbs

A simple CI gate is often enough:

Invoke-ScriptAnalyzer -Path . -Recurse -EnableExit

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:

  1. Is this command retrieving, creating, updating, deleting, validating, importing, exporting, starting, or stopping something?
  2. Which approved verb most precisely matches that behavior?
  3. What is the actual domain object the command operates on?
  4. Is the noun singular, PascalCase, and scoped well for the module?
  5. Will related commands form a clean family around the same noun?
  6. 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:

  1. Use approved verbs and check them with Get-Verb.
  2. Use singular PascalCase nouns.
  3. Keep noun families consistent across the module.
  4. Avoid Invoke unless it's genuinely the most accurate verb.
  5. Align destructive or mutating verbs with SupportsShouldProcess.
  6. Treat PSScriptAnalyzer as part of the module contract, not as an optional cleanup step.
  7. 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.