Splatting is a really great feature in PowerShell that lets you take a [hashtable] of parameters and call a function or cmdlet with the hash instead of having to type out every parameter name and value. It seems natural then that if you’re writing a wrapper or proxy function, where your function takes the same or nearly the same parameters as the function you’re calling, you could use $PSBoundParameters to do the heavy lifting (this special variable contains all of the parameter values passed into the function).

The problem comes when your proxy function has defaults for its optional parameters. $PSBoundParameters only includes the values of parameters that were explicitly supplied by the caller. There is no such variable that contains the default values.

Workaround

I’ve created a small code snippet which alleviates this. It finds the parameters of your script or function, then gets the actual current values (you can override the supplied parameter values), and puts them all in a [hashtable].

$params = @{}
foreach($h in $MyInvocation.MyCommand.Parameters.GetEnumerator()) {
    try {
        $key = $h.Key
        $val = Get-Variable -Name $key -ErrorAction Stop | Select-Object -ExpandProperty Value -ErrorAction Stop
        if (([String]::IsNullOrEmpty($val) -and (!$PSBoundParameters.ContainsKey($key)))) {
            throw "A blank value that wasn't supplied by the user."
        }
        Write-Verbose "$key => '$val'"
        $params[$key] = $val
    } catch {}
}

Details

I’m using $MyInvocation.MyCommand.Parameters to get all of the parameter names, and then I’m using Get-Variable to get the actual current value of it.

That means any default value of a parameter will be counted. It was also especially useful for me because I was doing additional checking and in some cases changing the values of the parameters. This method will see the new values, so the resulting hash will be “complete”.

I found that in some cases Get-Variable threw an exception because it couldn’t find the variable. This was actually because of automatic parameters like $Verbose which actually translate into $VerbosePreference and the like, so these will not be included in the hash.

I also found that optional parameters which didn’t supply a default were being included in the hash. That’s the part where I check for $val being null or empty. I also check to see if it’s included in $PSBoundParameters and if not, it won’t be included.

This check is specifically for the case where you intentionally passed in a $null or an empty string, so that they won’t erroneously be excluded.

Splatting Example

Here’s a complete example with 2 functions.

function Test-Params {
[CmdletBinding()]
param(
    [Parameter()]
    [Int]
    $Optional1 ,

    [Parameter()]
    [String]
    $Optional2 = 'Value2' ,

    [Parameter(
        Mandatory=$true
    )]
    [Int]
    $Mandatory3 ,

    [Parameter()]
    [Int]
    $Optional4 = 100 ,

    [Parameter()]
    [Switch]
    $DoThing
)

    if($DoThing) {
        $Mandatory3 = $Mandatory3*2
    }

    $params = @{}
    foreach($h in $MyInvocation.MyCommand.Parameters.GetEnumerator()) {
        try {
            $key = $h.Key
            $val = Get-Variable -Name $key -ErrorAction Stop | Select-Object -ExpandProperty Value -ErrorAction Stop
            if (([String]::IsNullOrEmpty($val) -and (!$PSBoundParameters.ContainsKey($key)))) {
                throw "A blank value that wasn't supplied by the user."
            }
            Write-Verbose "$key => '$val'"
            $params[$key] = $val
        } catch {}
    }

    $params.Remove('DoThing')

    Test-Callee @params
}

function Test-Callee {
[CmdletBinding()]
param(
    [Parameter()]
    [Int]
    $Optional1 ,

    [Parameter()]
    [String]
    $Optional2 ,

    [Parameter(
        Mandatory=$true
    )]
    [Int]
    $Mandatory3 ,

    [Parameter()]
    [Int]
    $Optional4
)

    Write-Host $Optional1
    Write-Host $Optional2
    Write-Host $Mandatory3
    Write-Host $Optional4
}

Test-Params -Optional1 22 -Mandatory3 55

Test-Params -Optional1 22 -Mandatory3 55 -DoThing

Notes

Note that the proxy function here has an additional switch parameter called DoThing, and if that’s specified then we’re modifying the value of Mandatory3. Note that since the function we’re proxying doesn’t contain that parameter, we have to remove it from the hash before splatting.

The output of the above script is:

22
Value2
55
100
22
Value2
110
100

Happy scripting!