An advanced function (often called a script cmdlet) is a PowerShell function that behaves identically to compiled binary cmdlets. By decorating a standard function with the [CmdletBinding()] attribute, you unlock standard cmdlet features, including common parameters (-Verbose, -Debug, -ErrorAction), parameter validation attributes, pipeline integration, and lifecycle execution blocks (begin, process, and end).
The [CmdletBinding()] Attribute
Adding [CmdletBinding()] directly above the param() block transforms a standard function into an advanced function. This attribute automatically exposes PowerShell's common parameters without requiring you to declare them manually:
- Information and Debugging:
-Verbose,-Debug,-InformationAction,-InformationVariable - Error and Warning Handling:
-ErrorAction,-ErrorVariable,-WarningAction,-WarningVariable - Output and Pipeline:
-OutVariable,-OutBuffer,-PipelineVariable - Impact Mitigation:
-WhatIf,-Confirm(whenSupportsShouldProcess = $trueis specified)
Furthermore, [CmdletBinding()] provides access to the $PSCmdlet runtime variable and forces the function into strict mode, throwing errors if callers pass undeclared parameters.
Enabling Verbose Output with [CmdletBinding()]
The following script illustrates how [CmdletBinding()] enables the built-in -Verbose switch.
function Test-ServiceHealth {
[CmdletBinding()]
param (
[string]$ServiceName
)
Write-Verbose "Querying system for service status: $ServiceName"
$service = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
if ($null -eq $service) {
Write-Warning "Service '$ServiceName' does not exist on this host."
return
}
Write-Verbose "Service found. Current state: $($service.Status)"
[PSCustomObject]@{
Name = $service.Name
Status = $service.Status
}
}
Test-ServiceHealth -ServiceName "Spooler"
Test-ServiceHealth -ServiceName "Spooler" -Verbosewhere:
[CmdletBinding()]declares this function as an advanced function, automatically injecting standard common parameters.param ([string]$ServiceName)defines the function parameter.Write-Verboseemits output to the verbose stream (stream 4). This message is suppressed by default and only displays when-Verboseis passed at execution time.Write-Warningemits a non-terminating warning message to stream 3.
./test-servicehealth.ps1function Test-ServiceHealth {
[CmdletBinding()]
param (
[string]$ServiceName
)
Write-Verbose "Querying system for service status: $ServiceName"
$service = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
if ($null -eq $service) {
Write-Warning "Service '$ServiceName' does not exist on this host."
return
}
Write-Verbose "Service found. Current state: $($service.Status)"
[PSCustomObject]@{
Name = $service.Name
Status = $service.Status
}
}
Test-ServiceHealth -ServiceName "Spooler"
Test-ServiceHealth -ServiceName "Spooler" -VerboseParameter Validation Attributes
Advanced functions allow you to enforce strict data contracts using parameter validation attributes. These attributes validate arguments before any script logic in the function body executes, eliminating the need for defensive boilerplate code.
Key validation attributes include:
[ValidateNotNullOrEmpty()]ensures the supplied value is neither$nullnor an empty string or empty collection.[ValidateSet('Val1', 'Val2', ...)]restricts allowable values to a predefined list. Tab completion automatically surfaces these valid options.[ValidateRange(Min, Max)]restricts numeric values or collections of numbers to a specific numerical range.[ValidatePattern('Regex')]verifies the parameter string matches a regular expression.[ValidateCount(Min, Max)]enforces the minimum and maximum number of elements in an array.
Enforcing Parameter Constraints
The following script defines a deployment function with validation decorators.
function Set-AppEnvironment {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$ApplicationName,
[Parameter(Mandatory = $true)]
[ValidateSet("Development", "Staging", "Production")]
[string]$Environment,
[ValidateRange(1, 16)]
[int]$InstanceCount = 1
)
Write-Host "Deploying $InstanceCount instance(s) of '$ApplicationName' to '$Environment'..." -ForegroundColor Cyan
}
Set-AppEnvironment -ApplicationName "AuthService" -Environment "Staging" -InstanceCount 4where:
[Parameter(Mandatory = $true)]causes PowerShell to prompt the user if the argument is omitted from the command line.[ValidateNotNullOrEmpty()]rejects empty strings such as""or whitespace-only inputs.[ValidateSet(...)]restricts the$Environmentparameter strictly to"Development","Staging", or"Production".[ValidateRange(1, 16)]blocks any$InstanceCountbelow 1 or above 16.[int]$InstanceCount = 1provides a default value if the parameter is omitted.
./deploy-application.ps1Deploying 4 instance(s) of 'AuthService' to 'Staging'...When an invalid argument violates a constraint, PowerShell terminates execution during parameter binding before running the function body:
Set-AppEnvironment -ApplicationName "AuthService" -Environment "Sandbox" -InstanceCount 4Set-AppEnvironment : Cannot validate argument on parameter 'Environment'. The argument "Sandbox" does not
belong to the set "Development,Staging,Production" specified by the ValidateSet attribute. Supply an argument
that is in the set and then try the command again.Pipeline Input: Begin, Process, and End Blocks
To accept objects from the PowerShell pipeline, parameters must declare pipeline attributes, and the function body must be structured using lifecycle execution blocks: begin, process, and end.
Pipeline Lifecycle Blocks
begin {}runs once per function call before any pipeline data arrives. Use this block for initialization tasks (e.g., establishing database connections, opening log files, resetting tracking variables).process {}runs once for each object passed through the pipeline. If data is passed as a standard parameter argument rather than via the pipeline, theprocessblock runs exactly once. All per-item business logic belongs here.end {}runs once after all pipeline items have passed through theprocessblock. Use this block for teardown and cleanup operations (e.g., closing file streams, flushing buffers, displaying aggregate statistics).
+-------------------------------------------------------+
| Pipeline Stream: Object1 -> Object2 -> Object3 |
+-------------------------------------------------------+
|
v
+-------------------------------------+
| begin { } (Executes 1 time) |
+-------------------------------------+
|
v
+-------------------------------------+
| process { } (Executes 3 times) |
+-------------------------------------+
|
v
+-------------------------------------+
| end { } (Executes 1 time) |
+-------------------------------------+Configuring Pipeline-Aware Parameters
Parameters accept pipeline input using one or both of these properties:
ValueFromPipeline = $truebinds input objects matching the parameter's type.ValueFromPipelineByPropertyName = $truebinds to a specific property of an input object if the property name matches the parameter name.
Simple Pipeline Example
The following function squares each number piped into it.
function Get-SquaredValue {
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline = $true)]
[int]$Number
)
process {
$Number * $Number
}
}
1, 2, 3, 4 | Get-SquaredValuewhere:
[Parameter(ValueFromPipeline = $true)]allows$Numberto receive values piped into the function.process { }runs once per pipeline item.
1
4
9
16Implementing a Pipeline-Enabled Function
The following script accepts server names from both standard parameter arguments and the pipeline, inspecting each host in the process block.
function Test-NetworkHost {
[CmdletBinding()]
param (
[Parameter(
Mandatory = $true,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true
)]
[Alias("CN", "ServerName")]
[string[]]$ComputerName
)
begin {
Write-Verbose "=== Initializing Connectivity Verification Suite ==="
$results = [System.Collections.Generic.List[PSCustomObject]]::new()
}
process {
foreach ($computer in $ComputerName) {
Write-Verbose "Testing reachability for: $computer"
$pingSuccess = Test-Connection -TargetName $computer -Count 1 -Quiet
$results.Add([PSCustomObject]@{
ComputerName = $computer
Reachable = $pingSuccess
Timestamp = (Get-Date)
})
}
}
end {
Write-Verbose "=== Finalizing and Emitting Results ==="
$results
}
}
"127.0.0.1", "localhost" | Test-NetworkHost
[PSCustomObject]@{ ComputerName = "127.0.0.1" } | Test-NetworkHostwhere:
ValueFromPipeline = $truepermits direct piping of raw strings into the$ComputerNameparameter.ValueFromPipelineByPropertyName = $trueextracts theComputerNameproperty automatically from piped objects.[Alias("CN", "ServerName")]maps alternative property names (such asCNfrom Active Directory objects) directly to$ComputerName.[string[]]$ComputerNameis typed as an array to handle multiple values whether piped individually or passed via-ComputerName host1,host2.begin {}initializes the$resultscollection before any pipeline records arrive.process {}iterates over the current pipeline object bound to$ComputerName.end {}emits the collected results back to the pipeline after stream processing finishes.
pwsh ./test-networkhost.ps1ComputerName Reachable Timestamp
------------ --------- ---------
127.0.0.1 True 9/28/2026 12:15:18 AM
localhost True 9/28/2026 12:15:18 AM
127.0.0.1 True 9/28/2026 12:15:18 AMPractical Usage: Building Cmdlet-Like Functions
Combining [CmdletBinding()], validation, and pipeline blocks produces a function that feels indistinguishable from a built-in cmdlet.
Temperature Conversion with Pipeline Support
function Convert-Temperature {
[CmdletBinding()]
param(
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[ValidateNotNullOrEmpty()]
[double]$Celsius,
[ValidateSet("Fahrenheit", "Kelvin")]
[string]$To = "Fahrenheit"
)
process {
switch ($To) {
"Fahrenheit" { [PSCustomObject]@{ Celsius = $Celsius; Fahrenheit = ($Celsius * 9/5) + 32 } }
"Kelvin" { [PSCustomObject]@{ Celsius = $Celsius; Kelvin = $Celsius + 273.15 } }
}
}
}
20, 25, 30 | Convert-Temperature -To FahrenheitCelsius Fahrenheit
------- ----------
20.00 68.00
25.00 77.00
30.00 86.00Supporting -WhatIf and -Confirm
Production administrative scripts should avoid making destructive changes blindly. By specifying SupportsShouldProcess = $true inside [CmdletBinding()], PowerShell adds -WhatIf and -Confirm parameters to your function automatically, allowing users to preview or verify actions before execution.
Inside the function, wrap dangerous actions in an if ($PSCmdlet.ShouldProcess(...)) condition.
function Clear-ApplicationTempData {
[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
param (
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[ValidateNotNullOrEmpty()]
[string]$FolderPath
)
process {
if (-not (Test-Path -LiteralPath $FolderPath)) {
Write-Warning "Target path '$FolderPath' does not exist."
return
}
$targetFiles = Get-ChildItem -Path $FolderPath -File -Recurse -ErrorAction SilentlyContinue
foreach ($file in $targetFiles) {
if ($PSCmdlet.ShouldProcess($file.FullName, "Delete permanent application temp file")) {
Remove-Item -LiteralPath $file.FullName -Force
Write-Verbose "Successfully removed: $($file.FullName)"
}
}
}
}
Clear-ApplicationTempData -FolderPath "C:\AppLogs\Temp" -WhatIfwhere:
SupportsShouldProcess = $trueimplements standard-WhatIfand-Confirmfunctionality on the function.ConfirmImpact = 'High'informs PowerShell that this operation is potentially destructive. If the system's$ConfirmPreferenceis set to High or lower, the function automatically prompts for confirmation even if-Confirmwas not explicitly passed.$PSCmdlet.ShouldProcess($target, $action)intercepts execution. If-WhatIfwas passed, it prints the target and action without executing the code inside theifblock. If-Confirmwas passed, it prompts the user with[Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend.
./clear-tempdata.ps1What if: Performing the operation "Delete permanent application temp file" on target "C:\AppLogs\Temp\cache_01.dat".
What if: Performing the operation "Delete permanent application temp file" on target "C:\AppLogs\Temp\session_99.tmp".Best Practices
- Use
[CmdletBinding()]on any function intended for reuse across scripts or modules. - Always structure pipeline-enabled functions with
begin,process, andendblocks. - Prefer parameter validation attributes over manual
ifchecks to keep functions self-documenting and safe. - Implement
SupportsShouldProcessfor any function that modifies system state. - Avoid
$global:variables; use parameters and return values for data flow.