Skip to content

Common -Splat parameter #25574

Closed as duplicate
Closed as duplicate

Description

@iRon7

Summary of the new feature / enhancement

The proposal for inline splatting has been rejected some time ago.

See: #25029:

This was first proposed a long time ago:

Rejected RFC: https://github.com/PowerShell/PowerShell-RFC/blob/master/Archive/Draft/RFC0002-Generalized-Splatting.md

Related discussion: PowerShell/PowerShell-RFC#6
A related PR that was therefore ultimately not accepted: #10073

As far I understand the issue behind the rejection: the discussion was meanly focused on an operator that could pass an unnamed argument which would require a semantic sugar in the engine that might conflict with other syntaxes or initiate a break change.
If the splatting could be done by means of a common named parameter (e.g. -Splat [Object[]]), the impact on the enige would be less and I guess easier to implement, while this would be more PowerShell compliant and support inline usage:

Wishful thinking:

New-AzVm -Splat @{
    Name = $Name
    Location = "West US"
    ResourceGroupName = "myResourceGroup"
    VirtualNetworkName = "myVnet"
    SubnetName = "mySubnet"
    SecurityGroupName = "myNetworkSecurityGroup"
    PublicIpAddressName = "myPublicIpAddress"
}

Proposed technical implementation details (optional)

No response

Activity

  1. added
    Issue-Enhancementthe issue is more of a feature request than a bug
    Needs-TriageThe issue is new and needs to be triaged by a work group.
    on May 16, 2025
  2. mklement0 commented on May 16, 2025

    @mklement0
    Contributor

    Great idea, iRon7.

    As with any introduction of a new common parameter, breaking existing scripts that use the same parameter name for custom purposes is a concern, and a quick search here on GitHub reveals quite a few that already use -Splat.

    Thus, perhaps -PSSplat is the better choice (an analogous search currently finds no preexisting uses).

  3. iRon7 commented on May 17, 2025

    @iRon7
    Author

    Michael Klement (@mklement0),

    Thank you for your feedback (and the way to check search repositories).
    Other alternative names, I could think of:

    • -SplatParameters which abbreviates to -Splat and -SplatParam
    • -BindParameters which abbreviates to -Bind and -BindParam. In addition to this, it could have a possible -Splat alias.
      (Abbreviations and aliases get afaik a lesser priority as an actual parameter name).
  4. MartinGC94 commented on May 17, 2025

    @MartinGC94
    Contributor

    What would happen if you splat the splat parameter?

    $Params = @{
        Path = "C:\"
        Splat = @{
            LiteralPath = "C:\"
        }
    }
    gci @Params
    
  5. jborean93 commented on May 17, 2025

    @jborean93
    Collaborator

    What would happen if you splat the splat parameter?

    Add a few more levels and use Splat in the Splat in the splat :)

  6. mklement0 commented on May 17, 2025

    @mklement0
    Contributor

    iRon7:

    Good idea; -SplatParameters seems like a better choice (I'd definitely keep the word "splat" in there), as the "PS" prefix has no precedent among the common parameters; -SplatParameters too yields currently no code-search results.

    Just to clarify: I presume that by abbreviated you're simply referring to the "elastic syntax" feature, i.e. the ability to situationally specify an unambiguous name prefix for targeting any parameter by name.
    (Formally declaring e.g. -Splat as an alias of -SplatParameters wouldn't work.)

  7. anonhostpi commented on May 17, 2025

    @anonhostpi

    BEFORE we get into any disagreements:

    I'm gonna say it now (to prevent the exact same conversation that kicked off in the RFC):

    I think that we would all be happy if there was anyway to inline splat than no way at all...

  8. kasini3000 commented on May 17, 2025

    @kasini3000
    1. I think it's possible to write a notice in "powershell next version.MSI" stating that 'psteam' will use 'splash' as the public keyword.

    2. Establish a preview version function switch.

  9. 237dmitry commented on May 17, 2025

    @237dmitry

    Overall, I liked this idea. Exactly, as an idea. But if you consider it in a practical plane, then doubts arise about the expediency of its application. If you take the interactive mode, then it is rather inconvenient, and in scripts there is no need to save on variables. The only area of ​​application is script blocks or copy-paste to the console.

    Although it must be said that splatting allows you to structure the set of parameters.

  10. mklement0 commented on May 17, 2025

    @mklement0
    Contributor

    237dmitry

    in scripts there is no need to save on variables.

    There is: Any construct that forces you to use an auxiliary variable that otherwise serves no purpose deserves improving to eliminate that need - indeed, that's what drove the original RFC.

  11. 237dmitry commented on May 18, 2025

    @237dmitry

    Any construct that forces you to use an auxiliary variable that otherwise serves no purpose deserves improving to eliminate that need

    The point is comfort syntax. I do not believe that you do want type splattings in interactive shell:

    $ dir -sp @{ lp = "/some/dir"; filter = "*.ext";  s = $true }
  12. iRon7 commented on May 20, 2025

    @iRon7
    Author

    237dmitry, in a enhancement propose, it is not about what the propose doesn't add but what it does add.
    Which in this case is mainly support for inline splatting without violating the best practices:

    As with the current implementation, there is probably less need for splatting in the interactive shell (where readability is generally less of a issue as it isn't being reused). Anyways, aside from reusing splatted parameters in different commands, I can imaging situation where you could prevent some redundancy, e.g. in a situation where you have a list of parameter collection that you want to apply on a specific cmdlet as New-AzVm:

    $Json = Get-Content .\NewVMs.json
    (ConvertFrom-Json $Json -AsHashTable).Values | Foreach-Object { New-AzVm @_ }

    To visualize this:

    $Json = '{
      "Line1": { "BackgroundColor": "Black", "ForegroundColor": "Green",  "NoNewline": true, "Message": "Foo" },
      "Line2": { "BackgroundColor": "Blue",  "ForegroundColor": "Yellow", "NoNewline": false, "Message": "Bar" }
    }'
    
    (ConvertFrom-Json $Json -AsHashTable).Values | Foreach-Object { Write-Host @_ }

    Saying that, if this propose is generally accepted, I would prefer it to support (PSCustom)Objects (#6889) rather than the current splatting (embedded) unnamed parameters (arrays) feature as that is hard to combine and the latter could lead to complex and unmanageable for cmdlet parameters as ConvertTo-Csv -Splat $MyObject, ';', especially if:

    In other words, keep it imple and lean by only supporting (embedded) named (key-value pairs) splat parameters, like: ConvertTo-Csv -Splat @{ InputObject = $MyObject; Delimiter = ';' } . To test this idea, I have created a (quick and dirty) prototype which isn't common but might be used to manually add a -SplatParameters parameter to an existing cmdlet to check the syntax:

    Add-SplatParameter

    Add-SplatParameter
    function Add-SplatParameter {
        param (
            [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
            [ValidateNotNull()][string]$CommandName
        )
        $Command = (Get-Command $CommandName -All).where({ $_.Source }, 'first')[0]
        $MetaData = [System.Management.Automation.CommandMetaData]$Command
        $ProxyCommand = [System.Management.Automation.ProxyCommand]::Create($MetaData)
        $Ast = [System.Management.Automation.Language.Parser]::ParseInput($ProxyCommand, [ref]$null, [ref]$null)
    
        $StringBuilder = [System.Text.StringBuilder]::new()
        $Null = $StringBuilder.AppendLine("function Global:$($Command.Name) {")
        if ($Ast.ParamBlock -and $Ast.ParamBlock.Parameters.get_Count()) {
            $Offset = $Ast.ParamBlock.Parameters.Extent[-1].EndOffset
            $Null = $StringBuilder.Append($ProxyCommand.SubString(0, $Offset))
            $Null = $StringBuilder.AppendLine(',')
            $Null = $StringBuilder.AppendLine()
            $Null = $StringBuilder.AppendLine('    $SplatParameters')
            $Null = $StringBuilder.AppendLine(')')
        }
    
        $Null = $StringBuilder.AppendLine('    begin {')
        $Null = $StringBuilder.Append('        $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand(')
        $Null = $StringBuilder.Append("'$($Command.Source)\$($Command.Name)'")
        $Null = $StringBuilder.Append(', [System.Management.Automation.CommandTypes]::Cmdlet)')
        $Null = $StringBuilder.AppendLine({
            $Parameters = [Ordered]@{} # Overrule the bound - and splat parameters depending on their order
            foreach ($BoundKey in $PSBoundParameters.get_Keys()) {
                if ($BoundKey -eq 'SplatParameters') {
                    if ($SplatParameters -is [System.Collections.iDictionary]) {
                        foreach ($SplatKey in $SplatParameters.get_Keys()) {
                            $Parameters[$SplatKey] = $SplatParameters[$SplatKey]
                        }
                    }
                    else {
                        foreach ($SplatName in $SplatParameters.PSObject.Properties.Name) {
                            $Parameters[$SplatName] = $SplatParameters.PSObject.Properties[$SplatName].Value
                        }
                    }
                }
                else {
                    $Parameters[$BoundKey] = $PSBoundParameters[$BoundKey]
                }
            }
            $scriptCmd = {& $wrappedCmd @Parameters }
            try {
                $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
                $steppablePipeline.Begin($PSCmdlet)
            } catch {
                throw
            }
        }.ToString())
        $Null = $StringBuilder.AppendLine('    }')
        $Null = $StringBuilder.AppendLine({
        process {
            try {
                $steppablePipeline.Process($_)
            } catch {
                throw
            }
        }
    
        end {
            try {
                $steppablePipeline.End()
            } catch {
                throw
            }
        }
    
        clean
        {
            if ($null -ne $steppablePipeline) {
                $steppablePipeline.Clean()
            }
        }}.ToString())
        $Null = $StringBuilder.AppendLine('}')
        Invoke-Expression $StringBuilder.ToString()
    }

    Example 1: Reuse splatted parameters in different commands

    Add-SplatParameter Write-Host
    
    $Colors = @{ForegroundColor = "black"; BackgroundColor = "white"}
    
    #Write a message with the colors in $Colors
    Write-Host "This is a test." -Splat $Colors
    
    #Write second message with same colors. The position of splatted
    #hash table does not matter.
    Write-Host -Splat $Colors "This is another test."

    Example 2: Inline Splatting

    Write-Host "This is a test." -Splat @{
        ForegroundColor = "black"
        BackgroundColor = "white"
    }

    Example 3: Override bound parameters:

    Write-Host "This is a test." -ForegroundColor Blue -Splat @{ ForegroundColor = "black"; BackgroundColor = "white" }

    Example 4: Override splat parameters:

    Write-Host "This is a test." -Splat @{ ForegroundColor = "black"; BackgroundColor = "white" } -ForegroundColor Blue

    Example 5: Loading a list of parameter object collections

    $ParameterCollection = # Import-Csv ...
    ConvertFrom-Csv @'
    BackgroundColor, ForegroundColor, Object
    Black,           Green,           Foo
    Blue,            Yellow,          Bar
    '@
    $ParameterCollection | Foreach-Object { Write-Host -Splat $_ }

    A more realistic use case would probably be Creating Users with a CSV
    where if you fully prepare the source CSV file, you might do:

    Import-Csv -Path .\users.csv | Foreach-Object { New-ADUser -Splat $_ }

    Example 6: Inline (multiple lines without using backticks) translating parameter object collections

    $ParameterCollection = # Import-Csv ...
    ConvertFrom-Csv @'
    Back,  Fore,   Newline, Message
    Black, Green,  False,   Foo
    Blue,  Yellow, True,    Bar
    '@
    
    $ParameterCollection | Foreach-Object {
        Write-Host -Splat @{
            BackgroundColor = $_.Back
            ForegroundColor = $_.Fore
            NoNewline       = -Not [Bool]::Parse($_.Newline)
            Object          = $_.Message
        }
    }

    The syntax in the later examples might also be used to forward parameters using $PSBoundParameters

    MartinGC94,

    What would happen if you splat the splat parameter?

    I think that a cyclic splat parameters should be neglected or eventually produce an error.

    $Params = @{
        Path = "C:\"
        Splat = @{
            LiteralPath = "C:\"
        }
    }
    gci @Params

    Apart from the fact that -Path and -LiteralPath are mutual exclusive for Get-ChildItem anyways, I see no harm in using the suggested named -Splat parameters in the currently existing splatting feature (although I didn't implement this in the Add-SplatParameter prototype). In other words, I see no issues in your suggestive questions, but don't hesitate to further elaborate if you think there is really a (unresolvable) concern here.

  13. 237dmitry commented on May 20, 2025

    @237dmitry

    The only area of ​​application is script blocks or copy-paste to the console

    To visualize this:

    And script-block is visualized

    But with pipelining it will be convenient, no need to delete variables or localize their scope.

  14. MartinGC94 commented on May 20, 2025

    @MartinGC94
    Contributor

    My only 2 concerns are:
    1: Normal functions/scripts don't get common parameters, so if I define a function like this: function MyFunction ($Test){} I won't be able to use this new splat parameter.
    2: It's weird to add a new common parameter that interacts slightly differently with existing systems (splatting) than every other parameter.

    The obvious solution to 1 would be to just add the common parameter regardless if it's a basic or advanced function, but that would be yet another example of the odd inconsistency mentioned in the second problem.

    That's why I'd personally prefer to see a new syntax like: ls -@{X="Y"} or ls @@{X="Y"} rather than a new common parameter.

  15. 2 remaining items

  16. iRon7 commented on May 21, 2025

    @iRon7
    Author

    Michael Klement (@mklement0),

    Note that example 1 is already supported

    Indeed, it was just to show the basic syntax as compared to the (documented) current implementation

    Making splatting support [pscustomobject] instances as an alternative to `[hashtable] ...
    so I encourage you to open a new issue

    I am little reluctant to do that as I fear a complex rabbit hole in combination with the splatting with arrays feature where any item in the array might be a [PSCustomObject] ($InputObject) by itself. That is why I actually like to avoid this particular feature from this specific propose.
    Also knowing that the general best practice is to Avoid Using Positional Parameters. To my opinion the splatting-with-arrays feature already affects the readability of the Splatting-with-hash-tables, take e.g.

    $Settings = @{
        Severity = @('Error', 'Warning')
        IncludeRules = 'PSAvoid*'
        ExcludeRules = '*WriteHost'
    }
    Invoke-ScriptAnalyzer $pshome\Modules\BitLocker @Settings -IncludeRules 'MyRules'

    vs:

    $Splat = @{ Settings = $Settings }, $pshome\Modules\BitLocker
    Invoke-ScriptAnalyzer $pshome\Modules\BitLocker @Splat 

    And will probably be more confusing in combination with a support of Splatting-with-PSCustomObjects feature.

    MartinGC94,

    1: Normal functions/scripts don't get common parameters

    That is valid point (and that also includes external commands) but note that for basic functions you will also lose other common parameters as e.g. -?.

    2: It's weird to add a new common parameter that interacts slightly differently with existing systems (splatting)

    That is true, but improvements require change (where you might debate whether it is indeed an improvement).

    Anyways, thank you both for you feedback.

  17. mklement0 commented on May 21, 2025

    @mklement0
    Contributor

    iRon7, just to close the [pscustomobject] tangent: array- and hashtable-based splatting are already mutually exclusive [update: in the context of a single instance of splatting]: when you use an array for splatting (which is useful primarily when calling external programs, though even there it is isn't strictly necessary to use splatting at all), its elements are passed as-is as individual arguments.
    In other words: you (sensibly) cannot mix these two forms of splatting [in a single instance of splatting], and there is therefore also no conceptual confusion.
    My suggestion was to simply allow [pscustomobject]s as an alternative to hashtable-based splatting.

  18. anonhostpi commented on May 21, 2025

    @anonhostpi

    MartinGC94

    I'd personally prefer to see a new syntax like: ls -@{X="Y"} or ls @@{X="Y"} rather than a new common parameter.

    Be careful of what you wish for.

    -splat vs @@ debate has already taken place and is exactly what caused the RFC to fail (debate could never get resolved).

    Not trying to discourage discourse, but it might be wiser to unify on one syntax than debating it (given the circumstances).

    Otherwise, they'll close this issue just like they did the RFC

  19. IISResetMe commented on May 21, 2025

    @IISResetMe
    Collaborator

    Michael Klement (@mklement0) mixing different types of splatting targets in the same invocation works just fine:

    $color = @{ ForegroundColor = 'Cyan' }
    $msgs = 'Hello','World!'
    
    Write-Host @msgs @color
  20. mklement0 commented on May 21, 2025

    @mklement0
    Contributor

    Mathias R. Jessen (@IISResetMe), indeed it does.
    That's not what I was talking about, however: I was talking about (sensibly) not being able to mix the two approaches in a single instance of splatting, such as expecting array elements that happen to be hashtables to be splatted in a nested fashion (I've updated my previous comment to clarify).

    P.S.: Not only have I personally never seen multiple instances of splatting in the context of a given, single command, doing so is, in my estimation, ill-advised.

  21. iRon7 commented on May 22, 2025

    @iRon7
    Author

    Michael Klement (@mklement0),

    array- and hashtable-based splatting are already mutually exclusive

    You are right, I was under the assumption that they could also be mixed. Still, I see some issues with supporting both array and PSCustomObject based splatting (also knowing that PowerShell tents to unroll single item arrays).
    Meaning any PSCustomObject scalar could perceived as a unnamed position 0 argument, like the way it works now:

    $Object = ConvertFrom-Csv @' # or often $inputObject at position 0
    BackgroundColor, ForegroundColor, Object
    Black,           Green,           Foo
    '@
    
    Write-Host @Object
    @{BackgroundColor=Black; ForegroundColor=Green; Object=Foo}
    

    Or as PSCustomObject based splatting (as suggested):

    $Splat = ConvertFrom-Csv @'
    BackgroundColor, ForegroundColor, Object
    Black,           Green,           Foo
    '@
    
    Write-Host @Splat
    Foo # where Foo is written in green

    The differents with hashtable based splatting is that there a virtually no cmdlets that accept a hashtable a position 0.

  22. IISResetMe commented on May 22, 2025

    @IISResetMe
    Collaborator

    iRon7

    also knowing that PowerShell tents to unroll single item arrays

    Does the following example illustrate your core concern here?

    $listOfArgs = @([pscustomobject]@{ Param = 'Value' })
    $splatArgs = $listOfArgs |Select -First 2 # oh no, there's only 1 output object, $splatArgs is implicitly a scalar
    
    Do-Something @splatArgs # unexpected object-splatting instead of array-splatting
    
  23. iRon7 commented on May 22, 2025

    @iRon7
    Author

    Mathias R. Jessen (@IISResetMe),

    Exactly, in fact the concern already plays with the current implementation, for function with parameters like:

    Function Get-HashTable {
        Param(
            [Parameter(Position = 0)]
            [HashTable]$HashTable,
            [Parameter(Position = 1)]
            [String]$string
        )
    
        Write-Host 'HashTable:' $hashTable 'String:' $String
    }
    
    $Splat = @{ String = 'Item' }, 'String' | Select -First 2
    Get-HashTable @Splat
    HashTable: [String, Item] String: String
    
    $Splat = @{ String = 'Item' }, 'String' | Select -First 1
    Get-HashTable @Splat
    HashTable:  String: Item # Expected: HashTable: [String, Item]

    The impact is probably low (and resolvable with $Splat = @($Splat)), but there is in fact one native cmdlet that is currently affected by this confusion:

    Get cmdlets that could lead to array splatting confusion ```PowerShell foreach ($Command in Get-Command *) { if ($Command.Parameters) { $PSCustomObjectAt0, $SomeThingAt1 = $null foreach($Parameter in $Command.Parameters.get_Values()) { if ( $Parameter.ParameterType -eq [System.Collections.iDictionary] -and 0 -eq $Parameter.Attributes.Position ) { $PSCustomObjectAt0 = $true } if (1 -eq $Parameter.Attributes.Position) { $SomeThingAt1 = $True } if ($PSCustomObjectAt0 -and $SomeThingAt1) { $Command; break } } } } ```
    CommandType     Name                                               Version    Source
    -----------     ----                                               -------    ------
    Cmdlet          Add-Member                                         7.0.0.0    Microsoft.PowerShell.Utility

    And for PSCustomObject based splatting (change $Parameter.ParameterType -eq [System.Collections.iDictionary] to $Parameter.ParameterType -eq [PSCustomObject]) the affected cmdlets (aside from any potential custom cmdlet) are:

    CommandType     Name                                               Version    Source
    -----------     ----                                               -------    ------
    Cmdlet          ConvertTo-Csv                                      7.0.0.0    Microsoft.PowerShell.Utility
    Cmdlet          Register-ObjectEvent                               7.0.0.0    Microsoft.PowerShell.Utility
    Cmdlet          Unprotect-CmsMessage                               7.0.0.0    Microsoft.PowerShell.Security

    And I am not even covering the (lower impact but higher complexity) confusion that might raise with parameters sets (that could have different parameter positions per set)

  24. IISResetMe commented on May 22, 2025

    @IISResetMe
    Collaborator

    Thanks for clarifying iRon7

    I'm personally of the mind that that's a non-issue, or at least one we can document ourselves out of - if you want to use advanced syntactical features like the splatting operator I think it's fair to also expect you familiarize yourself with the semantics (as is already the case, as shown in your example).

  25. jborean93 commented on May 27, 2025

    @jborean93
    Collaborator

    I'm attempting to take another stab at splatting enhancements in PowerShell and have opened a summary of the various options proposed over the years and am hoping to get feedback from anyone interested. If you are interested in having a look and/or wanting to comment on some of the options I would love any feedback at jborean93/PowerShell-RFC#1.

    The aim is to get more community consensus around the various options before settling on a real RFC based on the feedback and going from there.

  26. iRon7 commented on May 28, 2025

    @iRon7
    Author

    Michael Klement (@mklement0), Mathias R. Jessen (@IISResetMe),

    I needed some time to adjust my feelings about this and I agree now that as Mathias stated, the bears on the road I saw are probably non-issues. Meaning (MartinGC94 ) that I think that a possible new splat parameter should indeed interact similar as the existing system (splatting) implementation toward array-based splatting (and possible even reuse some code behind this).
    For opening a new issue on PSCustomObject-based splatting, I still like to hold off a bit and see how the Add enhanced splatting RFC draft #1 from Jordan is perceived and evolves.

    Thanks all for your feedback and thoughts on this.

  27. SeeminglyScience commented on Jul 2, 2025

    @SeeminglyScience
    Contributor

    Centralizing all of the issues about inline splatting in #25721

  28. microsoft-github-policy-service commented on Jul 2, 2025

    @microsoft-github-policy-service
    Contributor

    📣 Hey @iRon7, how did we do? We would love to hear your feedback with the link below! 🗣️

    🔗 https://aka.ms/PSRepoFeedback

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Issue-Enhancementthe issue is more of a feature request than a bug

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions