Every PowerShell function has a little secret. When you call it with parameters, it quietly writes them down on a notepad before running the code. Most of the time we never look at that notepad… but once you discover it, you’ll wonder how you ever scripted without it.

That notepad is called $PSBoundParameters.

It’s an automatic hashtable that contains exactly what the caller typed: the parameter names and the values they passed. Nothing more, nothing less.

Why is that so exciting? Because with $PSBoundParameters, you can:

  • See precisely which parameters were used,
  • Forward them to other commands without rewriting your function,
  • And even tweak them on the fly before passing them along.

In this post, we’re going to shine a light on this overlooked gem of PowerShell. Along the way, we’ll write a few demo functions, experiment with what $PSBoundParameters captures (and what it doesn’t), and explore real-world tricks for logging, forwarding, and customizing parameters.

Think of it as giving your scripts a memory of the conversation they just had with the user. 🧠

During this post, you’ll see this icon 🎬 whenever it’s time for you to try something in your own PowerShell session.

And you’ll see this 📒 when I’m explaining the why behind the example, so you understand the details in the bigger picture.

So let’s crack open this automatic variable and see how it really works.

In our toolbelt

The automatic variable of the day is called … 🥁🥁🥁 $PSBoundParameters
Think of it as the flight recorder of your function. ✈️
Every time someone calls your function and passes in parameters, $PSBoundParameters quietly records only what they said out loud, not assumptions, not defaults, just the actual knobs they turned.

Why is that handy? Imagine you’re building functions that:
• Pass work along to another command,
• Need to log exactly what was asked for,
• Or behave differently depending on whether the user explicitly set something.
Instead of writing if statements everywhere, you just peek into $PSBoundParameters and know instantly what the caller wanted.


📒 This is why advanced modules lean on $PSBoundParameters: it’s like a translator between the user’s intent and your script’s logic.

First encounter with $PSBoundParameters

Alright, enough theory! Let’s crack this open and see what it actually does. 🔍

To keep things simple, we’ll write a little function that accepts a few parameters and then dumps whatever $PSBoundParameters recorded for us.

🎬 Try this in your PowerShell session:

function Show-BoundParams {
    param (
        [string]$Name,
        [int]$Age,
        [string]$City = "Unknown"
    )
    Write-Host "Here's what was bound:"
    $PSBoundParameters
}
  • Now call the function with the code below
Show-BoundParams -Name "Alice" -Age 30

📒 Notice something interesting? The output only shows Name and Age. The parameter City is missing! even though we defined it,  because you didn’t explicitly pass it.

That’s the beauty of $PSBoundParameters: it only keeps track of what the user actually provided, not what defaults you silently filled in! Super cool! For instance, when letting a user set initial script variables, globals, or, for instance, verbose logging.

  • Your shell should look like below

Cool right?! So it already differentiates user variables provided through input or variables which are specified with default values!

Forwarding parameters

Here’s where $PSBoundParameters really earns its superhero cape 🦸‍♂️.

Imagine you’re writing a function that wraps another command, say Get-Process. Normally, you’d have to manually pass every parameter along:

Get-Process -Name $Name -Verbose:$Verbose

…but what if your function has ten or twenty parameters? That’s a lot of repetitive typing and a headache waiting to happen.

Enter $PSBoundParameters. With a simple splat, you can forward all the parameters the user passed without retyping them:

function Get-MyProcess {
    [CmdletBinding()]
    param (
        [string]$Name
    )

    # Inspect what was actually passed
    Write-Host "Parameters user actually provided:"
    $PSBoundParameters

    # Use Write-Verbose to show extra info
    Write-Verbose "Fetching processes with the following parameters: $($PSBoundParameters | Out-String)"

    # Forward all user-provided parameters to Get-Process
    Get-Process @PSBoundParameters
}

🎬 Make sure you have notepad open and call it like this:

  • Get-MyProcess -Name “notepad”
  • Get-MyProcess -Name “notepad” -Verbose

📒 What happens:

  • The first call runs normally, $PSBoundParameters shows only Name, and Write-Verbose does nothing (since -Verbose wasn’t passed).
  • The second call automatically passes the -Verbose switch to the function and to Get-Process. Write-Verbose triggers, showing extra info about what the function is doing, all without manually checking for -Verbose.

This is the real power of $PSBoundParameters: your wrapper can dynamically accept and forward switches like -Verbose to underlying commands while still letting the cmdlet handle them as intended.

Now with nested functions

Now imagine we have two functions with a similar overlap of parameters and we don’t want to supply all the parameters each time ourselves manually. Then this variable really comes in handy!

🎬 Run the code below

function Write-Log {
    param(
        [string]$Message,
        [string]$Level = "Info"
    )    
    Write-Host "Parameters passed: $($PSBoundParameters.Keys -join ', ')"
    Write-Host "[$Level] $Message"
}

function Send-Notification {
    param(
        [string]$Message,
        [string]$Level = "Alert"
    )
    Write-Log @PSBoundParameters
}

 🎬Now call the functions

  • Write-Log -Message “System started”
  • Send-Notification -Message “Error occurred” -Level “Error”

Great right?! We don’t have to map all those variables ourselves anymore as the dictionary of PSBoundParameters already has these thing in it!

Summary

That’s a wrap! 🎉


In this blog, we uncovered the hidden power of $PSBoundParameters in PowerShell, the automatic variable that quietly remembers exactly what the user passed into your functions 🧠.

We started by understanding how $PSBoundParameters only captures parameters explicitly provided by the caller, ignoring defaults unless specified. This is crucial when building flexible, user-friendly functions without endless if checks.

Then we explored parameter forwarding, showing how a simple splat can pass all user-provided values, including switches like -Verbose,  to underlying cmdlets or functions. No repetitive typing, no error-prone manual mapping. Just clean, dynamic, and elegant scripting.

Next, we dove into nested and connected functions, demonstrating how $PSBoundParameters lets one function feed another seamlessly. Whether you’re logging messages, sending notifications, or orchestrating complex tasks, this variable ensures your parameters are accurately captured, forwarded, and used, all without extra boilerplate.

📒 This post highlights how $PSBoundParameters can make your scripts smarter, DRY-er, and more maintainable, giving you a simple but powerful way to respect the user’s intent and handle parameters dynamically.

🎬 If you followed along, you now have a solid framework to inspect, log, forward, and manipulate function parameters in real-time, ready to extend into your own scripts and modules.

Happy scripting! 🚀

Leave a Reply

Your email address will not be published. Required fields are marked *