Skip to content

Functions

Scope: defining functions, parameters, and pipeline input. All examples were run with PowerShell 7.5.

Basic functions

function Get-Greeting {
    param([string]$Name = "stranger")
    return "Hello, $Name!"
}

Get-Greeting -Name "Ada"   # Hello, Ada!
Get-Greeting               # Hello, stranger!
  • Verb-Noun naming, same as cmdlets.
  • param() declares parameters with optional types and defaults.
  • Call with spaces and dashes, not parens: Get-Greeting -Name "Ada", never Get-Greeting("Ada").

Output is return

In PowerShell, everything the function outputs is its return value — there's no separate return channel:

function Get-Numbers {
    Write-Host "working..."   # screen only, NOT returned
    1, 2, 3                   # this IS returned
}

$result = Get-Numbers   # $result = 1, 2, 3

This is the #1 surprise for newcomers: stray output (a command you forgot to capture, a Write-Output) becomes part of the return value. Write-Host is safe (screen only); bare values and uncaptured commands are returned.

return "x" is just "output x and exit now" — convenient, but output-before-it counts too.

Advanced functions: cmdlet behavior

function Get-Even {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory, ValueFromPipeline)]
        [int]$Number
    )
    process {
        if ($Number % 2 -eq 0) { $Number }
    }
}

[CmdletBinding()] gives your function cmdlet superpowers: -Verbose, -WhatIf, -Confirm, common parameters. The begin/process/end blocks handle pipeline input — process runs once per piped object:

1..6 | Get-Even   # 2, 4, 6

Write advanced functions for anything shared; simple functions for quick helpers.

Parameter validation

function Set-Mode {
    param(
        [ValidateSet("on", "off")]
        [string]$State,

        [ValidateRange(1, 10)]
        [int]$Level = 5
    )
    Write-Host "$State at level $Level"
}

[ValidateSet()] restricts to known values (with tab completion!), [ValidateRange()] bounds numbers, [ValidateNotNullOrEmpty()] rejects empties. Validation beats manual if checks.

ShouldProcess: -WhatIf support

function Remove-OldLogs {
    [CmdletBinding(SupportsShouldProcess)]
    param([string]$Path)
    if ($PSCmdlet.ShouldProcess($Path, "Delete old logs")) {
        Remove-Item "$Path\*.log" -ErrorAction SilentlyContinue
    }
}

With SupportsShouldProcess, -WhatIf shows what would happen without doing it — the safe way to write destructive functions.

Check

  • [ ] You define function Verb-Noun with param() and call with -Name value syntax
  • [ ] You can explain why stray output becomes return data
  • [ ] You know what [CmdletBinding()] buys you

Next: Objects and the Pipeline.