Functions
By the end of this lesson you'll wrap reusable logic in named functions with typed, validated, and mandatory parameters, accept input straight from the pipeline, and understand exactly what a function returns — so your tools behave like real cmdlets instead of leaking surprises.
Learn Python, JavaScript, Java and more with free interactive lessons, real projects and AI-powered help. Beginner-friendly.
Part of the free Powershell course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
A function is a kitchen appliance . A blender has labelled inputs (the lid, the speed dial) — those are your param() entries. Some inputs are required: you can't blend without the jug attached, just like [Parameter(Mandatory)] . You feed ingredients in through the top one at a time — that's pipeline input arriving in the process {' '} block. And the appliance produces one thing: the smoothie that pours out. In PowerShell, everything you pour out is the result — so if you accidentally drop a stray spoon in the jug, it comes out in the smoothie too. That "accidental spoon" is the output-pollution bug you'll learn to avoid.
1. The function keyword & param() blocks
A function is a named, reusable block of code. You write function Verb-Noun {' '} — the same Verb-Noun naming PowerShell uses for built-in cmdlets like Get-Date , which makes your tools feel native. Inputs go in a param() block, where each parameter can declare a type (like [string] or [int] ) and a default value . Crucially, you call a function the shell way: space-separated, dash-named arguments like Get-Greeting -Name "Alice" — not with commas in brackets. Read this worked example and run it.
Your turn. The function below is almost complete — fill in the ___ blanks using the hints, then run it. Notice how a typed parameter with a default lets the caller skip arguments.
2. Required input with [Parameter(Mandatory)] & [CmdletBinding()]
Sometimes a parameter is essential and there's no sensible default. Mark it [Parameter(Mandatory)] and PowerShell will refuse to run without it — if the caller forgets, the shell interactively prompts for the value instead of crashing. Adding [CmdletBinding()] at the top of the param() block promotes your function to an advanced function : it instantly gains the common parameters every real cmdlet has, like -Verbose and -ErrorAction . Write-Verbose messages only appear when the caller asks for them with -Verbose , and they never mix into your data output.
3. Pipeline input: ValueFromPipeline + process {' '}
PowerShell's superpower is the pipeline ( | ), and your functions can plug straight into it. Two pieces make it work: mark a parameter [Parameter(ValueFromPipeline)] so values are allowed to arrive from the pipe, and put your logic in a process {' '} block, which runs once for every item that comes down the pipeline. Inside process , your parameter (and the automatic variable $_ ) holds the current item. Skip the process block and your function only ever sees the last piped item — a very common beginner bug.
Your turn. Combine what you've learned: make Get-Square accept numbers from the pipeline and require a label. Fill in the three blanks.
4. return and what a function really outputs
Here's the idea that trips up everyone coming from C#, Python, or JavaScript: in PowerShell, every value a function emits is part of its return value — not just the thing after return . A bare "Starting..." on its own line is emitted and ends up in the output. return $x doesn't mean "this is THE result"; it means " stop the function now and emit $x ". To send a value back you simply leave it un-assigned. To print a human message without polluting the output, use Write-Host or Write-Verbose , which write to separate channels.
In the first example, $result captured both "Starting..." and 6 because both were emitted. The fix is to never leave a stray value un-handled: assign it, pipe it to Out-Null , or use Write-Host / Write-Verbose for messages. This is the single most common reason a PowerShell function "returns weird extra stuff".
Variables you create inside a function are local to it — they vanish when the function ends and don't leak out to the caller. That's a good thing: it keeps your functions self-contained.
The twist is that a function can read a variable from the surrounding (parent) scope, but if it assigns to one, it creates a new local copy and leaves the original untouched. So this prints 1 , not 99 :
Prefer passing values in as parameters and returning results out — relying on outer variables makes functions fragile. If you truly must change a caller's variable, that's what [ref] parameters or explicit $script: / $global: scopes are for, but reach for them rarely.
No blanks this time — just a brief and an outline. Build a pipeline-friendly function from scratch, run it, and check your output against the example in the comments. This is exactly the shape of a real reusable tool.
Practice quiz
Which keyword defines a named function in PowerShell?
- function
- def
- sub
- func
Answer: function. You declare a function with the function keyword, e.g. function Get-Greeting { }.
Where do you declare a function's parameters?
- In an args block
- In a param() block
- In the function name
- In a return statement
Answer: In a param() block. Parameters go in a param() block, e.g. param([string]$Name = 'World').
How should you call a function in PowerShell?
- Get-Greeting('Alice')
- Get-Greeting{Alice}
- Get-Greeting -Name 'Alice'
- Get-Greeting=Alice
Answer: Get-Greeting -Name 'Alice'. PowerShell uses space-separated, dash-named arguments, not commas in brackets.
What does [Parameter(Mandatory)] do?
- Sets a default value
- Hides the parameter
- Adds verbose output
- Forces the caller to supply a value
Answer: Forces the caller to supply a value. Mandatory makes the parameter required, prompting if it is omitted.
Which two pieces let a function accept pipeline input?
- ValueFromPipeline plus a process {} block
- Mandatory plus return
- CmdletBinding plus begin
- param plus end
Answer: ValueFromPipeline plus a process {} block. Mark the parameter ValueFromPipeline and put logic in a process {} block.
In PowerShell, what becomes a function's output?
- Only the value after return
- Every value the function emits
- Only Write-Host text
- Nothing unless return is used
Answer: Every value the function emits. Every un-assigned value a function emits is part of its output.
What does the return keyword actually do?
- Marks the only result
- Suppresses all output
- Stops the function and emits what follows it
- Starts a loop
Answer: Stops the function and emits what follows it. return stops the function early and emits its value; it does not mark the sole result.
Which cmdlet prints a message without polluting data output?
- Return-Value
- Write-Verbose
- Emit-Data
- Echo-Line
Answer: Write-Verbose. Write-Verbose (or Write-Host) uses a separate channel from data output.
What does [CmdletBinding()] add to a function?
- A return type
- Faster execution
- Automatic parameters
- Common parameters like -Verbose and -ErrorAction
Answer: Common parameters like -Verbose and -ErrorAction. It promotes the function to an advanced function with common parameters.
Without a process {} block, a pipeline-input function sees which items?
- Every item
- The first item only
- No items
- Only the last piped item
Answer: Only the last piped item. Without process {}, the body runs once and only sees the last piped item.