Inconsistent behavior for splatting when using Invoke-Command
See original GitHub issueUsing splatting when calling Invoke-Command
has a different behavior than calling it without splatting. See my examples below.
The issue only seems to occur when $values
is assigned [System.Management.Automation.Internal.AutomationNull]::Value
(which is the case when there are no elements in the pipeline). If I hard code $values
to be $null
the code works as expected. Maybe [System.Management.Automation.Internal.AutomationNull]::Value
is not converted to a normal $null
when using splatting. 🤔
In the first case $Values
ends up being a [pscustomobject]
that foreach
is able to iterate over, something that is unexpected.
Steps to reproduce
Run the following, but change some-machine
to a proper remote machine.
function GetValues {
}
$values = GetValues
$invokeCommandParams = @{
ArgumentList = $values, "dummy"
}
Invoke-Command -ComputerName "some-machine" @invokeCommandParams {
param($Values, $Dummy)
Write-Host "Inside the first script block"
Write-Host "Is null: $($null -eq $Values)"
Write-Host "Is empty string: $('' -eq $Values)"
Write-Host "Type: $($Values.GetType())"
foreach ($value in $Values) {
Write-Host "Inside the loop. Value is $value"
}
}
Invoke-Command -ComputerName "some-machine" -ArgumentList $values, "dummy" {
param($Values, $Dummy)
Write-Host "Inside the second script block"
Write-Host "Is null: $($null -eq $Values)"
Write-Host "Is empty string: $('' -eq $Values)"
foreach ($value in $Values) {
Write-Host "Inside the loop. Value is $value"
}
}
Expected behavior
I expected both calls to Invoke-Command
to behave in the same way, which would have yielded the following output:
Inside the first script block
Is null: True
Is empty string: False
Type:
Inside the second script block
Is null: True
Is empty string: False
Actual behavior
Inside the first script block
Is null: False
Is empty string: True
Type: System.Management.Automation.PSCustomObject
Inside the loop. Value is
Inside the second script block
Is null: True
Is empty string: False
Environment data
Name Value
---- -----
PSVersion 7.2.0-preview.2
PSEdition Core
GitCommitId 7.2.0-preview.2
OS Microsoft Windows 10.0.19042
Platform Win32NT
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
WSManStackVersion 3.0
It get the same behavior on PowerShell 5 aswell.
Issue Analytics
- State:
- Created 3 years ago
- Reactions:2
- Comments:5 (2 by maintainers)
Top GitHub Comments
Yes, the common denominator is the cross-process (whether locally or across machine boundaries) serialization infrastructure, which also applies to the
$using:
“scope”; again, the problem can also be demonstrated with a background job:Splatting only surfaces the symptom, because it seems to bypass code used in direct argument binding that (surprisingly) converts
[System.Management.Automation.Internal.AutomationNull]::Value
to$null
. Similarly, no such conversion happens with$using:
.Without having done thorough analysis, here’s my guess as to why this happens:
In-process it is reference equality based on the singleton
[System.Management.Automation.Internal.AutomationNull]::Value
that is used to detect such values (and treats them as a collection with no properties and no elements).The serialization infrastructure is apparently not aware of this special value, and serializes it as a regular
[psobject]
instance (with a static, property-less[System.Management.Automation.PSCustomObject]
instance as its base object). The rehydrated regular[pscustomobject]
instance is obviously not the same as[System.Management.Automation.Internal.AutomationNull]::Value
in the target process, and therefore not recognized as such.In short: Given that the in-process
[System.Management.Automation.PSCustomObject]
has no distinguishing characteristics other than being a singleton, fixing this would require extending the serialization infrastructure to be aware of and correctly handle this value.However:
[System.Management.Automation.Internal.AutomationNull]::Value
does differ in its serialized form from an empty[psobject]
instance, so perhaps this existing differing representation is enough to unequivocally identify[System.Management.Automation.Internal.AutomationNull]::Value
on rehydration, which then wouldn’t require extending the remoting protocol:Note that parameter
[System.Management.Automation.Internal.AutomationNull]::Value
or even@([System.Management.Automation.Internal.AutomationNull]::Value)
alone wouldn’t work, because conversion to$null
/ no-element enumeration to an empty array would kick in.The above yields the following; note how the
[psobject]
instance is simply represented as an empty<Object>
element, unlike automation null:Another similar case is when the using scope is used together with
Invoke-Command
:This will result in the following:
This has nothing to do with splatting. But it might be caused by the same underlying circumstances as the splatting examples.