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.