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.
-
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. -
The
CommandParameterInternal
created from splatting a Hashtable needs to be marked as came-from-splatting. -
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
inStart-Sleep
), so the work should be done in the parameter binder. -
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 thePowerShell
instance, so we will need to add a boolean propertyCamFromSplatting
toCommandParameter
to track that information, which will be used when invoking thePowerShell
instance. -
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 generatedPowerShell
instance will be used to create a remote pipeline and run on the remote side, so the new member added toCommandParameter
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.