Skip to content
Howard van Rooijen By Howard van Rooijen Co-Founder
PowerShell Best Kept Secrets: Splatting

It's been over a year since I wrote An Omega Geek's Guide to Learning PowerShell, and I thought it was about time, with all the recent  noise about scriptcs, that PowerShell should get some more love as it's a much overlooked and misunderstood gem of the .NET and Windows ecosystem. When I announced in the office that I was working on a blog post about splatting, it was met with cries of "what on earth is that?", which is pretty much the same reception I get whenever I mention splatting to anyone.

This incredibly powerful feature of PowerShell probably wouldn't be a best kept secret if it had a less ridiculous name.  If it was called by a more boring, yet descriptive name, such as "dynamic parameter matching" or even "parameter unfurling" it would at least give an indication as to what it actually did.

The standard way of passing values into PowerShell functions is via a named parameters, for example:

Function Print-TwoThings
{
    Param
    (
        [string]
        $First,
        
        [string]
        $Second
    )
    
    Write-Host ("{0} {1}!" -f $First, $Second)
}

Clear-Host
Print-TwoThings -First "Hello" -Second "World"

which results in the output:

Hello World!

This is perfectly fine if you only have a few parameters, but if you are writing more complicated scripts (the standard example is some form of WMI query which requires a large number of parameters) it can become quite unwieldy.

Programming C# 12 Book, by Ian Griffiths, published by O'Reilly Media, is now available to buy.

Splatting allows you to convert those named parameters into a dictionary or hash and pass them into the function. The keys in the dictionary or hash will be examined to see if the match the names of the parameters and they do the values will be passed in. So if we refactor the above script to use splatting, it becomes:

Function Print-TwoThings
{
    Param
    (
        [string]
        $First,
        
        [string]
        $Second
    )
    
    Write-Host ("{0} {1}!" -f $First, $Second)
}

$parameters = @{
    First = "Hello"
    Second = "World"
}

Clear-Host
Print-TwoThings @parameters

which results (as expected) in exactly the same output:

Hello World!

But the power of the splatting does not stop there. If you are working on more complicated PowerShell scripts, especially the kind that encapsulate workflow or automation steps (for deployments or environment provisioning) you generally have a top level orchestration function which then calls into a series of inner functions which carry out individual steps.

Without splatting it's very cumbersome to try and pass values down through the call stack. But if you combine splatting with the power of the @PSBoundParameters then you can pass the top level parameter collection down through the layers:

Function Outer-Method
{
    Param
    (
        [string]
        $First,
        
        [string]
        $Second
    )
    
    Write-Host ($First) -NoNewline
    
    Inner-Method @PSBoundParameters
}

Function Inner-Method
{
    Param
    (
        [string]
        $Second
    )
    
    Write-Host (" {0}!" -f $Second)
}

$parameters = @{
    First = "Hello"
    Second = "World"
}

Clear-Host
Outer-Method @parameters
Discover your Power BI Maturity Score by taking our FREE 5 minute quiz.

which results (as expected) in exactly the same output:

Hello World!

The combination of these two features leads to some truly interesting possibilities. We have various ALM offerings around continuous deployment and environment provisioning, which has had enormous positive impact on our client's productivity.  PowerShell has always played a major part in our automation story, but orchestrating the various scripts has always been a bit messy. Splatting has allowed us to refactor and distil our scripts to the point where our configuration data almost looks like a DSL, which drives the individual automation tasks.

The example below simulates the steps required to provision an IIS Website, Application Pool and to configure HTTP and HTTPS bindings. All the configuration data is held in a hash at the bottom of the example and is simply splatted into the top level orchestration function, the values are then automatically passed down to inner functions via the use of @PSBoundParameters.

# This is the public script we call as an entry point into the deployment / configuration process
Function Invoke-Deployment
{
    Param
    (
        $ApplicationPool,
        $WebSite
    )
    
    Invoke-AppPoolTasks @PSBoundParameters
    Invoke-WebSiteTasks @PSBoundParameters
}

# The main script for managing ApplicationPools
Function Invoke-AppPoolTasks
{
    Param
    (
        # this is essentially a hashtable that contains all the properties we need
        # we can then use PS dot notation to step into the property we want to access
        # as if we had a fully fledged object model
        $ApplicationPool 
    )
    
    Write-Host $ApplicationPool.Name
}

# The main script for managing WebSites
Function Invoke-WebSiteTasks
{
    Param
    (
        $WebSite
    )
    
    Write-Host $Website.Name
    Write-Host $Website.ApplicationPoolName
    
    # we can access each property manually, for example $Website.Bindings[0].Ip or by looping
    foreach($binding in $Website.Bindings)
    {
        Write-Host ("{0}:{1}:{2}" -f $binding.Ip, $binding.Port, $binding.HostName)
    }
}

# We can define a stronger object model, which is essentially dynamic and easy to extend
$parameters = @{
    ApplicationPool = @{ 
        Name = "Endjin_Web_App_Pool" 
    }
    WebSite = @{ 
        Name = "Endjin_Web"
        ApplicationPoolName = "Endjin_Web_App_Pool"
        Bindings = @( 
            @{ 
                Ip = "192.168.100.10"
                Port = "80"
                HostName = "endjin.com"
            },
            @{ 
                Ip = "192.168.100.10"
                Port = "443"
                HostName = "endjin.com"
            }
        )
    }
}

Clear-Host
Invoke-Deployment @parameters

Which outputs the following:

Endjin_Web_App_Pool
Endjin_Web
Endjin_Web_App_Pool
192.168.100.10:80:endjin.com
192.168.100.10:443:endjin.com

As you can see, the combination of splatting, @PSBoundParameters and PowerShell's dot notation (which allows you to reference an element in a dictionary by it's key) allows you to create powerful, yet easy to understand PowerShell scripts.

@HowardvRooijen

Howard van Rooijen

Co-Founder

Howard van Rooijen

Howard spent 10 years as a technology consultant helping some of the UK's best known organisations work smarter, before founding endjin in 2010. He's a Microsoft ScaleUp Mentor, and a Microsoft MVP for Azure and Developer Technologies, and helps small teams achieve big things using data, AI and Microsoft Azure.