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-ObjectThis 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 ' -VerboseWithout -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 42If 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 -FullEven 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.
| Need | Useful mechanism |
|---|---|
| Opt-in diagnostics | [CmdletBinding()] + Write-Verbose/Write-Debug |
| Required input | [Parameter(Mandatory)] |
| Alternative valid shapes | Parameter sets |
| Input constraints | Validation attributes |
| Discoverable behavior | Get-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
| Mistake | Why it hurts | Better pattern |
|---|---|---|
Declare custom -Verbose | Conflicts with a common parameter | Use [CmdletBinding()] and Write-Verbose |
| Write diagnostics with plain output | Pollutes the data pipeline | Use diagnostic streams |
| Use one giant parameter set for conflicting modes | Invalid combinations reach the body | Model valid shapes as parameter sets |
| Add many attributes without a contract reason | Interface becomes hard to read | Use metadata to express actual behavior |
| Skip Get-Command/Get-Help inspection | Author view hides caller confusion | Test the function through discovery tools |
11. Knowledge check
Question 1. What marks a PowerShell function as an advanced function?
[CmdletBinding()] attribute, or use of equivalent parameter metadata that gives cmdlet-style behavior; this course uses [CmdletBinding()] explicitly.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?
$PSCmdlet.ParameterSetName.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
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.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this address.