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.
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.
What You'll Learn
💡 Real-World Analogy
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.
Watch the output channel
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".
Deep Dive: variable scope
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.
Common Errors (and the fix)
- Calling with commas/brackets like other languages: Get-Area(4, 3) does not pass two arguments — it passes one two-item array to the first parameter. PowerShell uses space-separated, dash-named args: Get-Area -Width 4 -Height 3 .
- Accidental output pollution: a stray line like "done" , or $list.Add(1) (which returns the index), gets added to your output. Symptom: $x = My-Func comes back as an array of unexpected things. Fix: assign it, pipe to | Out-Null , or use Write-Host / Write-Verbose for messages.
- Pipeline input only sees the last item: you used [Parameter(ValueFromPipeline)] but put the logic outside a process {} block, so it ran once on the final item. Fix: move the per-item work into process {} .
- Changing an outer variable from inside a function and it "doesn't stick": assigning inside a function makes a local copy. Return the new value and capture it ( $count = Bump $count ) instead of mutating the outer one.
- "A parameter cannot be found that matches parameter name 'X'": you passed a -Name the param() block doesn't declare (often a typo). Check the parameter spelling against the function definition.
📋 Quick Reference
Concept
Syntax
Define a function
function Verb-Noun { ... }
Typed param + default
param([int]$N = 1)
Required param
[Parameter(Mandatory)]
Pipeline input
[Parameter(ValueFromPipeline)] + process {}
Advanced function
[CmdletBinding()]
Return a value
just emit it (or return $x to stop early)
Call a function
Verb-Noun -Param value
Suppress output
expression | Out-Null
Frequently Asked Questions
Mini-Challenge: Convert-Temperature
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.
🎉 Lesson Complete!
- ✅ function Verb-Noun names reusable code; call it with space-separated, dash-named args
- ✅ param() declares typed parameters with defaults; [Parameter(Mandatory)] makes one required
- ✅ ValueFromPipeline + a process {} block let a function stream the pipeline, once per item
- ✅ Everything a function emits is returned; return only stops it early
- ✅ Use Write-Host / Write-Verbose for messages to avoid output pollution
- ✅ [CmdletBinding()] makes an advanced function with free common parameters
- ✅ Next lesson: File System and Registry — navigate and automate Windows storage
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.
Continue this course
- Previous: Working with Objects
- Next: File System and Registry