Chapter 09Lesson 02~145 minutes

Advanced Functions, CmdletBinding, and Common Parameters

Turn ordinary functions into cmdlet-like advanced functions with CmdletBinding, common parameters, diagnostics, metadata, and clear parameter sets.

Learning objectives

  • Explain what makes an advanced function cmdlet-like.
  • Use [CmdletBinding()] and inspect the common parameters it provides.
  • Send opt-in diagnostics through Write-Verbose and Write-Debug.
  • Use Parameter attributes and validation to define the command boundary.
  • Model alternative valid invocation shapes with parameter sets.
  • Inspect custom command metadata with Get-Command and Get-Help.

1. An advanced function participates in cmdlet-style command behavior

A normal PowerShell function is reusable code. An advanced function adds command metadata and behaviors that make the function act more like a compiled cmdlet. Microsoft documents advanced functions as a way to create cmdlet-like commands using PowerShell script rather than writing and compiling a .NET cmdlet.

function Get-TargetInfo {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$Name
    )

    [pscustomobject]@{ Name = $Name; CheckedAt = Get-Date }
}

The [CmdletBinding()] attribute is the marker. Once present, PowerShell supplies common cmdlet behaviors and exposes richer metadata through discovery tools.

2. CmdletBinding adds common parameters instead of forcing you to invent diagnostics switches

Advanced functions support common parameters such as -Verbose, -Debug, -ErrorAction, -WarningAction, -InformationAction, -OutVariable, and -PipelineVariable. You normally should not declare your own parameters with those names.

Get-Command Get-TargetInfo -Syntax
(Get-Command Get-TargetInfo).Parameters.Keys | Sort-Object

This makes your command feel consistent with built-in PowerShell commands. Users already know what -Verbose and -ErrorAction mean, so your function should participate in those conventions rather than inventing -ShowDetails or -IgnoreErrors.

3. Write-Verbose and Write-Debug are opt-in diagnostics, not unconditional console output

Diagnostic messages answer “what is the command doing internally?” They should not become normal data output. Write-Verbose and Write-Debug use dedicated streams controlled by common parameters and preference variables.

function Get-TargetInfo {
    [CmdletBinding()]
    param([Parameter(Mandatory)][string]$Name)

    Write-Verbose "Normalizing target '$Name'"
    $normalized = $Name.Trim().ToLowerInvariant()
    Write-Debug "Normalized value: $normalized"

    [pscustomobject]@{ Name = $normalized; CheckedAt = Get-Date }
}

Get-TargetInfo -Name ' API-01 ' -Verbose

Without -Verbose, the verbose diagnostic is normally suppressed. The function’s Success-stream output remains the structured target object either way. Chapter 10 will examine all PowerShell streams in depth.

4. Parameter attributes describe the contract at the command boundary

The [Parameter()] attribute lets you mark a value mandatory, assign it to a parameter set, configure pipeline binding, and provide other metadata. Validation attributes from Chapter 08 can be combined with it.

function Get-DeploymentSummary {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory, Position=0)]
        [ValidatePattern('^[a-z0-9-]+$')]
        [string]$Name,

        [ValidateSet('dev','test','prod')]
        [string]$Environment = 'dev'
    )

    [pscustomobject]@{ Name=$Name; Environment=$Environment }
}

For production automation, named invocation remains clearer even when a position is available. Parameter metadata makes the function discoverable; it should not make the interface cryptic.

5. Parameter sets express alternative valid command shapes

A parameter set is one valid combination of parameters. Use it when a command supports distinct modes that should not be mixed. For example, an inventory lookup might find a target either by name or by numeric ID.

function Get-InventoryTarget {
    [CmdletBinding(DefaultParameterSetName='ByName')]
    param(
        [Parameter(Mandatory, ParameterSetName='ByName')]
        [string]$Name,

        [Parameter(Mandatory, ParameterSetName='ById')]
        [int]$Id
    )

    [pscustomobject]@{
        ParameterSet = $PSCmdlet.ParameterSetName
        Name         = $Name
        Id           = $Id
    }
}

Get-InventoryTarget -Name api-01
Get-InventoryTarget -Id 42

If a caller supplies both -Name and -Id, PowerShell cannot select a valid parameter set and binding fails before the function body runs. That is preferable to accepting contradictory input and resolving it deep inside the implementation.

6. Get-Command and Get-Help expose your function as an inspectable command

Get-Command Get-InventoryTarget -Syntax
Get-Command Get-InventoryTarget | Select-Object Name,CommandType,ParameterSets
Get-Help Get-InventoryTarget -Full

Even before full comment-based help is added, PowerShell can derive syntax and parameter metadata from the function declaration. Lesson 5 adds the human explanations—synopsis, descriptions, examples, inputs, outputs, notes, and links—that metadata alone cannot supply.

7. Start documenting intent while the interface is still small

Comment-based help can live inside a function and is displayed by Get-Help. You will build a full help contract in Lesson 5; for now, notice that documentation sits next to the executable interface rather than in a separate wiki that can drift.

function Get-TargetInfo {
    <#
    .SYNOPSIS
    Returns normalized metadata for one target.

    .PARAMETER Name
    Target name to normalize and inspect.
    #>
    [CmdletBinding()]
    param([Parameter(Mandatory)][string]$Name)

    [pscustomobject]@{ Name = $Name.Trim().ToLowerInvariant() }
}

A shared function should eventually include examples and accurate input/output descriptions. Documentation is part of the interface, not an optional decoration.

8. Advanced does not mean complicated

Use advanced-function features to make behavior more predictable: common parameters instead of custom diagnostic flags, parameter sets instead of ambiguous modes, and metadata instead of hidden assumptions. Do not add attributes merely because they exist.

NeedUseful mechanism
Opt-in diagnostics[CmdletBinding()] + Write-Verbose/Write-Debug
Required input[Parameter(Mandatory)]
Alternative valid shapesParameter sets
Input constraintsValidation attributes
Discoverable behaviorGet-Command/Get-Help metadata

9. Lab: build an inspectable advanced function

Create a function with two parameter sets, validation, verbose diagnostics, and structured output. Then inspect it as a user would.

function Get-LabEndpoint {
    <# .SYNOPSIS Returns a simulated endpoint record. #>
    [CmdletBinding(DefaultParameterSetName='ByName')]
    param(
        [Parameter(Mandatory, ParameterSetName='ByName')]
        [ValidatePattern('^[a-z0-9-]+$')]
        [string]$Name,

        [Parameter(Mandatory, ParameterSetName='ByPort')]
        [ValidateRange(1,65535)]
        [int]$Port
    )

    Write-Verbose "Using parameter set $($PSCmdlet.ParameterSetName)"
    [pscustomobject]@{ ParameterSet=$PSCmdlet.ParameterSetName; Name=$Name; Port=$Port }
}

Get-Command Get-LabEndpoint -Syntax
Get-Help Get-LabEndpoint
Get-LabEndpoint -Name api-01 -Verbose
Get-LabEndpoint -Port 443
  • Get-Command shows two distinct syntax forms.
  • -Verbose changes diagnostics, not the Success-stream object shape.
  • An invalid port is rejected before the function body executes.
  • Supplying both Name and Port produces a parameter-set binding error.

10. Advanced-function mistakes and safer conventions

MistakeWhy it hurtsBetter pattern
Declare custom -VerboseConflicts with a common parameterUse [CmdletBinding()] and Write-Verbose
Write diagnostics with plain outputPollutes the data pipelineUse diagnostic streams
Use one giant parameter set for conflicting modesInvalid combinations reach the bodyModel valid shapes as parameter sets
Add many attributes without a contract reasonInterface becomes hard to readUse metadata to express actual behavior
Skip Get-Command/Get-Help inspectionAuthor view hides caller confusionTest the function through discovery tools

11. Knowledge check

Question 1. What marks a PowerShell function as an advanced function?

Question 2. Why use Write-Verbose instead of emitting a diagnostic string normally?

Question 3. What problem do parameter sets solve?

Question 4. Where can you inspect the selected parameter set at runtime?

Question 5. What can Get-Command reveal about an advanced function?

12. Summary

Advanced functions use cmdlet-style metadata to make reusable PowerShell commands more predictable. [CmdletBinding()] gives you common parameters and the $PSCmdlet context. Use Write-Verbose and Write-Debug for opt-in diagnostics, validation attributes for boundary rules, and parameter sets for alternative valid command forms. Inspect the command with Get-Command and Get-Help as part of development, because discoverability is part of correctness.

13. Further reading

Next lesson

Accept pipeline input deliberately and understand begin/process/end/clean lifecycle stages

Continue to Lesson 3, where the chapter builds on this command-design foundation with accept pipeline input deliberately and understand begin/process/end/clean lifecycle stages.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.