Defining Functions and Basic Parameters
A PowerShell function is declared with the function keyword followed by a name and a script block: function Verb-Noun { param($Name) ... }. PowerShell's naming convention pairs an approved verb (Get, Set, New, Remove, Test, etc., listed by Get-Verb) with a singular noun, which keeps function names predictable and lets Get-Command discover them consistently. Parameters are declared inside a param() block at the top of the function, and each can carry a type constraint like [string] or [int] that PowerShell enforces by attempting a conversion before the function body runs.
Cricket analogy: Like naming a fielding drill Get-CatchPractice rather than DoCatchStuff, the same verb-noun discipline the BCCI's coaching manuals use for named drills so coaches can find them instantly.
Parameter Validation and Attributes
Validation attributes placed above a parameter enforce constraints before the function body ever runs: [ValidateNotNullOrEmpty()] rejects null or empty strings, [ValidateSet('A','B','C')] restricts input to specific values (and also drives tab-completion), [ValidateRange(1,100)] bounds numeric input, and [ValidateScript({...})] runs custom logic returning $true or $false. Marking a parameter [Parameter(Mandatory)] forces PowerShell to prompt interactively for a value if the caller omits it, rather than letting the function run with $null.
Cricket analogy: Like a scoring app using [ValidateRange(0,6)] on $runsThisBall, rejecting an invalid value before it ever hits the scorecard, since no legal delivery scores more than 6 runs at once (barring overthrows).
Pipeline Input and ValueFromPipeline
A function accepts piped input by marking a parameter [Parameter(ValueFromPipeline=$true)] (for whole objects) or [Parameter(ValueFromPipelineByPropertyName=$true)] (to bind by matching property names). To process each piped item, the function needs a process {} block; without one, a function only receives the last object in the pipeline because a bare function body behaves like an end {} block that runs once after all input has arrived. begin {}, process {}, and end {} mirror the three phases available to ForEach-Object.
Cricket analogy: Like a scorer function with a process{} block updating the scorecard for every single ball piped in from a live feed, rather than only recording the final ball of the over.
Advanced Functions with CmdletBinding
Adding [CmdletBinding()] above param() turns an ordinary function into an advanced function that behaves like a compiled cmdlet: it gains common parameters (-Verbose, -Debug, -ErrorAction, -WarningAction), automatic support for -WhatIf and -Confirm when combined with SupportsShouldProcess=$true, and stricter parameter binding. Inside such a function, Write-Verbose and Write-Debug output is suppressed unless the caller passes -Verbose or -Debug, giving you diagnostic logging that doesn't clutter normal output. SupportsShouldProcess pairs with an explicit if ($PSCmdlet.ShouldProcess($target, $action)) check around any destructive operation.
Cricket analogy: Like a review system that only shows ball-tracking detail (-Verbose) when the third umpire explicitly requests it, keeping the broadcast clean for viewers who don't ask for that layer.
function Remove-OldLogFile {
[CmdletBinding(SupportsShouldProcess = $true)]
param(
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[ValidateNotNullOrEmpty()]
[string]$Path,
[Parameter()]
[ValidateRange(1, 365)]
[int]$OlderThanDays = 30
)
begin {
$cutoff = (Get-Date).AddDays(-$OlderThanDays)
Write-Verbose "Removing files older than $cutoff"
}
process {
$file = Get-Item -Path $Path -ErrorAction SilentlyContinue
if (-not $file) {
Write-Warning "Skipping missing file: $Path"
return
}
if ($file.LastWriteTime -lt $cutoff) {
if ($PSCmdlet.ShouldProcess($file.FullName, 'Delete log file')) {
Remove-Item -Path $file.FullName -Force
Write-Verbose "Deleted $($file.FullName)"
}
}
}
end {
Write-Verbose 'Cleanup complete.'
}
}
Get-ChildItem 'C:\Logs' -Filter *.log | Remove-OldLogFile -OlderThanDays 90 -WhatIfUse Get-Verb to list the ~100 PowerShell-approved verbs (Get, Set, New, Remove, Test, Invoke, etc.). Using an unapproved verb like Create- or Delete- still works, but PowerShell emits a warning when the module loads and it breaks the discoverability convention that Get-Command and tab-completion rely on.
Without a process {} block, a function that receives piped input only sees the LAST object in the pipeline, because the entire function body runs once as an implicit end block after all pipeline input has already been collected. This is a common source of bugs when converting a simple function into a pipeline-aware one.
- Functions follow Verb-Noun naming using PowerShell-approved verbs from Get-Verb for discoverability.
- Type-constrained parameters ([string], [int], etc.) trigger automatic conversion and rejection of incompatible input.
- Validation attributes like [ValidateSet], [ValidateRange], and [ValidateScript] enforce constraints before the function body runs.
- ValueFromPipeline and ValueFromPipelineByPropertyName let a function accept piped objects.
- A process {} block is required to handle every piped item; without it, only the last item is processed.
- [CmdletBinding()] adds common parameters (-Verbose, -Debug, -ErrorAction) and enables cmdlet-like behavior.
- SupportsShouldProcess with $PSCmdlet.ShouldProcess() enables -WhatIf and -Confirm for destructive operations.
Practice what you learned
1. What naming convention do PowerShell functions typically follow?
2. What happens if a mandatory parameter is not supplied when calling a function?
3. Why is a process {} block necessary for pipeline-aware functions?
4. What does adding [CmdletBinding()] to a function provide?
5. What is the purpose of $PSCmdlet.ShouldProcess() combined with SupportsShouldProcess?
Was this page helpful?
You May Also Like
Conditionals in PowerShell
Learn how PowerShell evaluates true/false logic with if/elseif/else, switch statements, and comparison/logical operators to control which code actually runs.
Loops in PowerShell
Master PowerShell's iteration constructs — foreach, for, while, do-while, and pipeline-based ForEach-Object — along with break and continue for controlling repetition.
Script Blocks and Scopes
Understand PowerShell script blocks as first-class values, how scoping (global, script, local, private) governs variable visibility, and how closures capture variables.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics