Reputation: 7638
Background: I have some locations I'd like to have easier access to. I don't want to mklink, because then something may save the path as the link path and fail later.
Proposed solution: Create some PSDrives. Start with a static array of hashtables, so that they can be imported/exported/filtered easily in the future.
Prep-step:
# Clear out existing drives, empty error stack
Get-PSDrive -name "*-test-*" | Remove-PSDrive
$Error.Clear()
Failed Solution 1: Create array of hashes with parameter KVPs
@(
@{ Name='hasharray-test-control'; Root='C:\Windows\Temp' },
@{ Name='hasharray-test-interp-brace'; Root="${ENV:SystemRoot}\Temp" }
) | New-PSDrive -PSProvider FileSystem
Confusing error "The input object cannot be bound to any parameters for the command ..."
Failed Solution 2: Web-grokking shows that the items have to be PSCustomObject, not a hash
@(
[PSCustomObject]@{ Name='array-test-control'; Root='C:\Windows\Temp' },
[PSCustomObject]@{ Name='array-test-interp-brace'; Root="${ENV:SystemRoot}\Temp" }
) | New-PSDrive -PSProvider FileSystem
Fails on second item with
New-PSDrive : Cannot bind parameter 'Name' to the target.
Exception setting "Name":
"Cannot process argument because the value of argument "value" is null.
Failed Solution 3: Ok, Maybe powershell is passing the array as one big blob, make it implicit
[PSCustomObject]@{ Name='implicitarray-test-control'; Root='C:\Windows\Temp' },
[PSCustomObject]@{ Name='implicitarray-test-interp-brace'; Root="${ENV:SystemRoot}\Temp" } |
New-PSDrive -PSProvider FileSystem
Fails on second item with same error
Failed solution 4: Insert intermediate variable
$TestDrives = @(
[PSCustomObject]@{ Name='foreach-test-control'; Root='C:\Windows\Temp' }
[PSCustomObject]@{ Name='foreach-test-interp-brace'; Root="${ENV:SystemRoot}\Temp" }
)
$TestDrives | New-PSDrive -PSProvider FileSystem
Fails on second item with same error
Failed Solution 5: Insert a select to extract properties
@(
[PSCustomObject]@{ Name='select-test-control'; Root='C:\Windows\Temp' },
[PSCustomObject]@{ Name='select-test-interp-brace'; Root="${ENV:SystemRoot}\Temp" }
) | Select Name, Root | New-PSDrive -PSProvider FileSystem
Fails on second item with same error
Failed Solution 6: No? Ok, I'll add a foreach to force each item to resolve
$TestDrives = @(
[PSCustomObject]@{ Name='foreachpipe-test-control'; Root='C:\Windows\Temp' },
[PSCustomObject]@{ Name='foreachpipe-test-interp-brace'; Root="${ENV:SystemRoot}\Temp" }
)
$TestDrives | ForEach-Object { $_ } | New-PSDrive -PSProvider FileSystem
Fails on second item with same error
Failed Solution 7: Still No? Fine, Select each property explicitly inside the foreach
@(
[PSCustomObject]@{ Name='foreachselect-test-control'; Root='C:\Windows\Temp' },
[PSCustomObject]@{ Name='foreachselect-test-interp-brace'; Root="${ENV:SystemRoot}\Temp" }
) | ForEach-Object { $_ | Select Name, Root } | New-PSDrive -PSProvider FileSystem
Fails on second item with same error
And just for good measure, let's try only static values:
@(
[PSCustomObject]@{ Name='foreachstatic-test-control'; Root='C:\Windows\Temp' }
[PSCustomObject]@{ Name='foreachstatic-test-control-2'; Root='C:\Windows\Temp' }
) | New-PSDrive -PSProvider FileSystem
Fails on second item with same error
Extra notes:
[PSCustomObject]@{ Name='test-interp'; Root="$HOME\Temp" }
[PSCustomObject]@{ Name='test-interp-command'; Root="$($HOME)\Temp" }
[PSCustomObject]@{ Name='test-format'; Root=('{0}\Temp' -f $HOME) }
[PSCustomObject]@{ Name='test-format-resolve'; Root=$('{0}\Temp' -f $HOME) }
$tRoot
outside hash, and use [PSCustomObject]@{ Name='test-format-resolve'; Root=$tRoot }
Working Solution: Wow, I'm speechless... let's force a foreach around everything
@(
[PSCustomObject]@{ Name='foreach-test-control'; Root='C:\Windows\Temp' },
[PSCustomObject]@{ Name='foreach-test-interp-brace'; Root="${ENV:SystemRoot}\Temp" }
) | ForEach-Object { $_ | New-PSDrive -PSProvider FileSystem }
And now it works, but just feels wrong, since it's already on the pipeline.
And now, the question(s):
Why do I have to explicitly ForEach-Object in this case instead of being able to use the pipeline, when New-PSDrive
supports ValueFromPipelineByPropertyName
for both Name
and Root
?
Why does the first item in each array work, while all subsequent items fail?
Upvotes: 4
Views: 2366
Reputation: 54881
This is a problem with the New-PSDrive
cmdlet, because atm. it only supports a single object at a time. Remove-PSDrive
does support array-input, which indicates that it should have been included in New-PSDrive
too (like PS-cmdlets is supposed to work). To verify that it's a bug with the cmdlet, you could run the following command to see how it properly binds the first object's property's, while it cancels at the beginning of the second object.
Trace-Command -Expression {
@(
[PSCustomObject]@{ Name='array-test-control'; Root='C:\Windows\Temp' },
[PSCustomObject]@{ Name='array-test-interp-brace'; Root="${ENV:SystemRoot}\Temp" }
) | New-PSDrive -PSProvider FileSystem
} -Name ParameterBinding -PSHost
Pieces of output:
#First object
BIND PIPELINE object to parameters: [New-PSDrive]
DEBUG: ParameterBinding Information: 0 : PIPELINE object TYPE = [System.Management.Automation.PSCustomObject]
DEBUG: ParameterBinding Information: 0 : RESTORING pipeline parameter's original values
DEBUG: ParameterBinding Information: 0 : Parameter [Name] PIPELINE INPUT ValueFromPipelineByPropertyName NO COERCION
DEBUG: ParameterBinding Information: 0 : BIND arg [hasharray-test-interp-brace] to parameter [Name]
DEBUG: ParameterBinding Information: 0 : BIND arg [hasharray-test-interp-brace] to param [Name] SUCCESSFUL
DEBUG: ParameterBinding Information: 0 : Parameter [Root] PIPELINE INPUT ValueFromPipelineByPropertyName NO COERCION
DEBUG: ParameterBinding Information: 0 : BIND arg [C:\Windows\Temp] to parameter [Root]
DEBUG: ParameterBinding Information: 0 : BIND arg [C:\Windows\Temp] to param [Root] SUCCESSFUL
#Second object
DEBUG: ParameterBinding Information: 0 : BIND PIPELINE object to parameters: [New-PSDrive]
DEBUG: ParameterBinding Information: 0 : PIPELINE object TYPE = [System.Management.Automation.PSCustomObject]
DEBUG: ParameterBinding Information: 0 : RESTORING pipeline parameter's original values
#It now skips right to the result(error) without trying to bind the parameters.
DEBUG: ParameterBinding Information: 0 : BIND PIPELINE object to parameters: [Out-Default]
Upvotes: 2
Reputation: 8019
You are hitting a bug in the implementation of New-PSDrive.
The property setters for the Name/Root/PSDrive parameters aren't written properly to support multiple objects in the pipeline.
Upvotes: 3