Where-Object filter unexpectedly matches the whole input collection, Get-Member shows collection type instead of element type Where-Object filter unexpectedly matches the whole input collection, Get-Member shows collection type instead of element type powershell powershell

Where-Object filter unexpectedly matches the whole input collection, Get-Member shows collection type instead of element type


It seems that Get-AzureStorSimpleAccessControlRecord is ill-behaved and sends its output collection as a whole through the pipeline, instead of item by item.
I've reported the issue here at windowsserver.uservoice.com, in case you want to vote to have it fixed.

You should be able work around that by enclosing the call in (...):

(Get-AzureStorSimpleAccessControlRecord) | Where-Object { $_.Name -like '*servertest4*' }

Enclosing in (...) makes PS convert the collection to a regular PS array up front, whose elements are then sent through the pipeline as expected; for background information, see this answer of mine.

Note, however, that this workaround invariably collects all elements up front before sending the items through the pipeline one by one.

If you want to avoid that, you can insert dummy pipeline stage | ForEach-Object { $_ }, which also forces item-by-item enumeration:

Get-AzureStorSimpleAccessControlRecord | ForEach-Object { $_ } |  Where-Object { $_.Name -like '*servertest4*' }

Optional reading: What happens without the workaround?

The following simplified example simulates Get-AzureStorSimpleAccessControlRecord's undesired behavior by creating an [int[]] array that is artificially wrapped in another array by way of the unary form of the array-construction operator, ,.
Sending this wrapped collection through the pipeline causes it to be sent as a whole instead of item by item.

$wrappedColl = , [int[]] (1, 2) # create artificially wrapped collection

Now, Get-Member reports the wrapped collection type instead of the type of the individual items, as normally happens (the pipeline unwrapped the outer array and sent its only element - the inner [int[]] array - as-is):

> $wrappedColl | Get-Member   TypeName: System.Int32[]  # !! Type of the wrapped array as a whole   ...

The array being passed as a whole also gets in the way when applying a Where-Object filter, which is what prompted the question:

# Trying to extract element 2 outputs the whole array(!).> $wrappedColl | Where-Object { $_ -eq 2 }12
  • Since the [int[]] array is passed as a whole, that's what $_ is bound to inside the Where-Object script block.

  • Applying -eq to a LHS that is an array performs array filtering rather than returning a simple [bool] value, so that [int[]] (1, 2) -eq 2 returns the array of matching items, which in this case is [object[]] 2 (note the result array is alway a regular PS array, of type [object[]]).

  • The resulting (single-item) array is then interpreted as a Boolean in the context of the Where-Object cmdlet, and any non-empty array evaluates to $true when coerced to a Boolean.

  • Therefore, since the script block passed to Where-Object evaluated to $true, the input object is passed through, which in this case is, as stated, the whole [int[]] array.