Advanced Functions & CmdletBinding¶
Advanced functions are PowerShell functions that behave exactly like compiled cmdlets — complete with common parameters (-Verbose, -Debug, -ErrorAction, -WhatIf, -Confirm, -OutBuffer, etc.).
The [CmdletBinding()] Attribute¶
Adding [CmdletBinding()] to your function declaration unlocks all common parameters:
function My-Function {
[CmdletBinding()]
param ()
process {
Write-Verbose "Running with verbose output"
}
}
My-Function -Verbose # now works
My-Function -Debug # now works
My-Function -ErrorAction Stop # now works
SupportsShouldProcess (–WhatIf and –Confirm)¶
Add SupportsShouldProcess to support -WhatIf and -Confirm for any function that makes changes:
function Remove-OldFiles {
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
param (
[Parameter(Mandatory)]
[string] $Path,
[int] $DaysOld = 30
)
$cutoff = (Get-Date).AddDays(-$DaysOld)
Get-ChildItem $Path -File |
Where-Object { $_.LastWriteTime -lt $cutoff } |
ForEach-Object {
if ($PSCmdlet.ShouldProcess($_.FullName, "Delete")) {
Remove-Item $_.FullName -Force
Write-Verbose "Deleted: $($_.FullName)"
}
}
}
# Preview
Remove-OldFiles -Path C:\Logs -DaysOld 7 -WhatIf
# Run with confirmation prompt
Remove-OldFiles -Path C:\Logs -DaysOld 7 -Confirm
# Run silently
Remove-OldFiles -Path C:\Logs -DaysOld 7 -Confirm:$false
PositionalBinding¶
Control whether positional binding is allowed:
[CmdletBinding(PositionalBinding = $false)]
# Now all parameters MUST be named — no positional usage
Parameter Sets¶
Use parameter sets to create mutually exclusive groups of parameters (like Get-ChildItem -File vs -Directory):
function Get-Report {
[CmdletBinding(DefaultParameterSetName = 'ByDate')]
param (
[Parameter(ParameterSetName = 'ByDate', Mandatory)]
[datetime] $StartDate,
[Parameter(ParameterSetName = 'ByDate')]
[datetime] $EndDate = (Get-Date),
[Parameter(ParameterSetName = 'ByCount', Mandatory)]
[int] $Last,
[Parameter(ParameterSetName = 'ByCount')]
[ValidateSet('Hours','Days','Weeks')]
[string] $Unit = 'Days'
)
# Detect which set was used
switch ($PSCmdlet.ParameterSetName) {
'ByDate' { "Report from $StartDate to $EndDate" }
'ByCount' { "Last $Last $Unit" }
}
}
Get-Report -StartDate "2024-01-01"
Get-Report -Last 7 -Unit Days
Dynamic Parameters¶
Add parameters at runtime based on conditions:
function Get-DriveInfo {
[CmdletBinding()]
param ()
dynamicparam {
$paramDict = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new()
# Only add -DriveLetter if on Windows
if ($IsWindows) {
$attr = [System.Management.Automation.ParameterAttribute]@{ Mandatory = $false }
$validateSet = [System.Management.Automation.ValidateSetAttribute](
(Get-PSDrive -PSProvider FileSystem).Name
)
$collection = [System.Collections.ObjectModel.Collection[System.Attribute]]@($attr, $validateSet)
$param = [System.Management.Automation.RuntimeDefinedParameter]::new('DriveLetter', [string], $collection)
$paramDict.Add('DriveLetter', $param)
}
return $paramDict
}
process {
$dl = $PSBoundParameters['DriveLetter']
if ($dl) {
Get-PSDrive -Name $dl
} else {
Get-PSDrive -PSProvider FileSystem
}
}
}
OutputType Declaration¶
Declare the return type so PowerShell's tab completion knows what properties to suggest:
function Get-ActiveProcess {
[CmdletBinding()]
[OutputType([System.Diagnostics.Process])]
param (
[switch] $Elevated
)
process {
Get-Process | Where-Object {
-not $Elevated -or $_.Handle
}
}
}
Verbose, Debug, and Progress¶
function Sync-Files {
[CmdletBinding()]
param (
[string] $Source,
[string] $Destination
)
$files = Get-ChildItem $Source -File
$total = $files.Count
$i = 0
foreach ($file in $files) {
$i++
Write-Progress -Activity "Syncing files" `
-Status "Processing $($file.Name)" `
-PercentComplete ($i / $total * 100)
Write-Verbose "Copying $($file.Name)"
Write-Debug " Source: $($file.FullName)"
Copy-Item $file.FullName $Destination -Force
}
Write-Progress -Activity "Syncing files" -Completed
}
Complete Advanced Function Template¶
function Verb-Noun {
<#
.SYNOPSIS
One-line description.
.DESCRIPTION
Full description.
.PARAMETER Name
Description of the Name parameter.
.EXAMPLE
Verb-Noun -Name "example"
Description of what this example does.
.INPUTS
System.String
.OUTPUTS
System.Management.Automation.PSCustomObject
.NOTES
Author: Your Name
Version: 1.0
.LINK
https://docs.example.com/verb-noun
#>
[CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'Default')]
[OutputType([PSCustomObject])]
param (
[Parameter(
Mandatory,
Position = 0,
ValueFromPipeline,
ValueFromPipelineByPropertyName,
HelpMessage = 'Enter the name',
ParameterSetName = 'Default'
)]
[ValidateNotNullOrEmpty()]
[string] $Name,
[Parameter(ParameterSetName = 'Default')]
[ValidateRange(1, 100)]
[int] $Count = 1,
[switch] $Force
)
begin {
Write-Verbose "[$($MyInvocation.MyCommand)] Starting"
}
process {
Write-Verbose "Processing: $Name"
if ($PSCmdlet.ShouldProcess($Name, "Operation")) {
[PSCustomObject]@{
Name = $Name
Count = $Count
Result = "Success"
}
}
}
end {
Write-Verbose "[$($MyInvocation.MyCommand)] Complete"
}
}