Skip to content

Allow explicitly specified named parameter to supersede the one from hashtable splatting #13114

Closed

Description

Problem Statement

Today, the splatting of a Hashtable in a PowerShell command invocation works just like explicitly specifying those key-value pairs as individual named parameters in the command line, so the following example doesn't work:

$common = @{
    AddressPrefix = "10.0.0.0/16"
    ResourceGroupName = "MyResourceGroup"
    Location = "westus"
}

New-AzVirtualNetwork @common -Name MyNet -AddressPrefix "10.0.0.0/24"

> New-AzVirtualNetwork: Cannot bind parameter because parameter 'AddressPrefix' is specified more than once. To provide 
> multiple values to parameters that can accept multiple values, use the array syntax. For example, "-parameter 
> value1,value2,value3".

$common may work well for all other 99 New-AzVirtualNetwork calls in my scripts, but there is one instance where a different address prefix is required. In such a case, I would have to temporarily change the Hashtable and restore it after the invocation, like this:

try {
    $commonAddrPrefix = $common.AddressPrefix
    $common.AddressPrefix = "10.0.0.0/24"

    New-AzVirtualNetwork @common -Name MyNet
}
finally {
    $common.AddressPrefix = $commonAddrPrefix
}

Proposed technical implementation details

The proposal is to allow an explicitly specified named parameter to supersede the same one that is expanded from splatting a Hashtable.

  1. To be clear, I want to point out that the proposal targets "Hashtable splatting" and "explicitly specified named parameters" only.
    (a). Array splatting won't change its current behavior.
    (b). Hashtable splatting won't change its current behavior when working with explicitly specified positional parameters.
    (c). Both Array splatting and Hashtable splatting keep their current behaviors when working with native command.

  2. The CommandParameterInternal created from splatting a Hashtable needs to be marked as came-from-splatting.

  3. To determine whether 2 named parameters are the same, parameters need to be resolved to cover the use of parameter alias or prefix (e.g. -s for -Seconds in Start-Sleep), so the work should be done in the parameter binder.

  4. Script-block-to-PowerShell conversion also does splatting. It calls powershell.AddParameter(string paramName, object paramValue) for each of the key-value pair of the Hashtable. The "came-from-splatting" message needs to be passed on to the PowerShell instance, so we will need to add a boolean property CamFromSplatting to CommandParameter to track that information, which will be used when invoking the PowerShell instance.

  5. Challenge: the script-block-to-PowerShell conversion is also used in remoting scenario when the script block to invoke on the remote side contains command invocations only, for example Invoke-Command { param($hash) dir @hash } -ArgumentList @{ path = "c:\" }. In that case, the generated PowerShell instance will be used to create a remote pipeline and run on the remote side, so the new member added to CommandParameter will likely cause serialization/deserialization problems. More investigation is needed.

Update to (5): powershell committee agreed (#13108 (comment)) to not use GetPowerShell in remote command execution, so (5) won't be a blocker.

/cc Steve Lee (@SteveL-MSFT) James Truher (@JamesWTruher) Joey Aiello (@joeyaiello)

Activity

  1. JamesWTruher commented on Jul 15, 2020

    @JamesWTruher
    Collaborator

    Additionally, we could provide new behavior where the position of the splatted provides a clue as to which values should be applied - here's an example of what I'm talking about:

    PS> function get-test {
    param (
    [Parameter()]$first,
    [Parameter()]$second
    )
    "$first : $second"
    }
    
    PS> $h = @{ first = 1; second = 2 }
    PS> get-test -first "what" @h
    1 : 2
    
    PS> get-test @h -first what
    what : 2
    

    in the first example get-test -first "what" @h the values in the splatted hashtable are used and override the explicit parameter use. in the second example get-test @h -first "what", since the explicit parameter is used after the splatted hashtable, the explicitly provided value is used. Configuration systems sometimes provide this behavior.

  2. PaulHigin commented on Jul 15, 2020

    @PaulHigin
    Contributor

    Having parameter argument overrides based on position in the argument list feels very confusing to me. It is something that is easy to forget, get wrong, and difficult to read and maintain. Having explicit parameter arguments override splatting arguments seems more intuitive to me.

  3. daxian-dbw commented on Jul 15, 2020

    @daxian-dbw
    MemberAuthor

    Having parameter argument overrides based on position in the argument list feels very confusing to me.

    I agree with Paul Higinbotham (@PaulHigin).
    I think it will cause burden to the script maintenance for sure. For example, when I need to add a few more named parameters to the following existing command call in a script, I have to figure out why the arguments were specified in that order, was it intentional or by accident? If it was by accident and it happened to work, then it may break when $hashConfig gets changed (e.g. added Param1 = arg0).

    Invoke-XXXX -Param1 args1 @hashConfig -Param2 arg2 -Param3 arg3
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 bugWG-Enginecore PowerShell engine, interpreter, and runtime

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions