1

In the creation of a script I'm writing, I ran across the use of what appears to be a pipeline shortcut that I have never seen before.

For example:

$acl | select -expandproperty Access | ? {$_.IdentityReference -eq "AD\$Group"}

     can be shortened to

$acl.Access.Where({$_.IdentityReference -eq "AD\$Group"}) 

What is this? $acl.Access makes sense to me as it's just a property reference but I do not see any such .Where() method being available on this object. As such it seems .Where({}) is some sort of pipeline shortcut for " | Where-Object {}". Where can I find documentation for such a thing? Do others like this exist?

Second question. I have a need to add a calculated property to my results, and within the property expression I have to perform piping as well, so where I would typically just reference $_ from the parent pipeline, this is lost within the expression statement.

I can get around this by using -PipelineVariable up within the pipeline, but it seems this only works when used with cmdlets and is not available when starting a pipeline with a variable (which I do often). Is there a way around this?

This works:

$acl = Get-Acl -Path "AD:\$dn"
$acl | select -expandproperty access -PipelineVariable AccessRight | ? {$_.IdentityReference -eq "AD\$Group"} | select *, @{N='ObjectTypeName';E={($ADGuidMap.GetEnumerator() | ? {$_.Value -eq $AccessRight.ObjectType}).Name}}  

I'd really like to be able to do this with the shortcut as anywhere I can shorten my oneliner I would like to do. Unfortunately the following will not work as I cannot use pipelinevariable at this location.

$acl.Access.Where({$_.IdentityReference -eq "AD\$Group"}) -PipeLineVariable AccessRight | select *, @{N='ObjectTypeName';E={($ADGuidMap.GetEnumerator() | ? {$_.Value -eq $AccessRight.ObjectType}).Name}} 

It's always bugged me about not being able to use Pipeline variables outside of cmdlets so I finally figured I'd ask if there was some sort of way around that.

1 Answer 1

2

As for the first question:

As an aside: You could have simplified your command even when using the Where-Object cmdlet, namely with simplified syntax:

$acl.Access | Where-Object IdentityReference -eq "AD\$Group"

As for the second question:

Because the .Where() method isn't a cmdlet, you cannot use the common
-PipelineVariable parameter
with it.

Given that .Access typically returns multiple objects, using the pipeline with -PipelineVariable enables an elegant solution.

If you do want to avoid the pipeline (operator), you can combine the .Where() and .ForEach() methods as follows, with the help of an intermediate (regular) variable:

$acl.Access.
  Where({ $_.IdentityReference -eq "AD\$Group" }).
  ForEach({
      $aclEntry = $_
      Select-Object -InputObject $aclEntry -Property *, 
        @{
          N = 'ObjectTypeName'
          E = { ($ADGuidMap.GetEnumerator().Where({ $_.Value -eq $aclEntry.ObjectType })).Name }
        }
    })

Update:

As you've discovered yourself, if you stick with a pipeline, you can combine -PipeLineVariable with Write-Output in order to capture each element of an array / collection in a pipeline variable as it flows through the pipeline, for use a later script block, as the following (contrived) example shows:

$array = 0..3
Write-Output $array -Pipeline arrayElement |
Where-Object { 0 -eq $_ % 2 } | # filter out odd numbers
 ForEach-Object { $_ + 1 } | # add 1
 ForEach-Object { "pipeline variable / `$_: $arrayElement / $_" }

Output, which shows that each element of input array $array was individually captured in pipeline variable $arrayElement:

pipeline variable / $_: 0 / 1
pipeline variable / $_: 2 / 3
4
  • Awesome. Thanks for the information. Just a clarification... so there's no way to use a pipelinevariable type situation if my command were to start with a simple variable, unless I do the intermediate variable within a foreach? Commented Dec 12, 2022 at 19:55
  • @MatthewMcDonald, if your command starts with a variable, you can also use that variable in the script blocks you pass to .Where() and .ForEach(). Here, the intermediate variable is needed to refer to the element at hand that is part of the collection stored in the starting variable. In simple cases, you can just use $_, without the need for an intermediate variable, but here you need one because of the nested .Where() call.
    – mklement0
    Commented Dec 12, 2022 at 20:08
  • You know, I was thinking if this is simply tied to a function, I immediately ask myself if there was a cmdlet I could use that would output the variable and ended up trying Write-Output. It worked! Write-Output $myvariable -pipelinevariable test Commented Dec 16, 2022 at 21:24
  • 1
    Apologies for delayed response. Yes. Thank you. Commented Jan 4, 2023 at 18:58

Not the answer you're looking for? Browse other questions tagged or ask your own question.