11/26/2017

Working with PowerShell’s -ExpandProperty (or… messing with the pipeline)


One of the great things about PowerShell is how easy and clear much of it is. But then there’s the other things that seem like magic. One of those is when you are dealing with objects that have properties that are collections of other objects.

Here will look at three solutions for dealing with properties that are collections of other objects:

  • Using -ExpandProperty (and a custom column or two)
  • Using ForEach (and a couple variables)
  • Using ForEach (and a customized object)


Get-DependentServices

Sorry, there’s not such a cmdlet. Get-Service does return service objects that have a dependent services property. The only problem is that it returns a summary of the dependent services, and only a partial list. While you could pipe this to Format-List -Wrap, that will not give you any details about the dependent services.

image


Using -ExpandProperty

You can use -ExpandProperty to expand any single property.

image

While the above works nicely for a single service, it creates a mess for a collection of services. Which DependentService belongs to which service?

image

Your first attempt to solve this will probably fail. In this case, it fails as both the Service object has a Name property and the DependentServices objects have a Name property.

image

Your second attempt may run without error, but we still can’t see which Service belongs with which DependentService. Adding a custom column seems to be ignored here:

image

This didn’t work as expected because a Service object has a default list of Select properties defined in an XML file somewhere. ( :-) )  Your custom column was passed through the pipeline, but as a NoteProperty (a PowerShell added custom property).

image

You will need to pipe the last attempt to another Select to see your NoteProperty.

image

So the steps are:

  • Create a custom column with the data to pass on down the pipeline.
       @{n="ParentServiceName";e={$_.Name}}
  • Expand the property with the collection of child objects.
       -ExpandProperty DependentServices
  • Pipe the child object and its new NoteProperty to a final Select.
       select ParentServiceName, Status, Name


Using ForEach with Variables

ExpandProperty works great when you need to expand a single property. When you need to futher manipulate the data before sending it on down the pipeline you may want to use ForEach, formally ForEach-Object.

    Get-Service | ForEach { some code } | Select ….

As a simple ForEach, let’s list Services in color based on Status.

   Get-Service | foreach { 
      if ($_.Status -eq "Running") 
         { Write-Host $_.Name -ForegroundColor Green } else
         { Write-Host $_.Name -ForegroundColor Red }
   }

image

Write-Host does not add its content to the pipeline, so that’s the end of the line for this one.

Using ForEach to solve our Service and DependentServices problem, we will create and then reuse a variable that contains the name of the service. (I call this “Store and Forward”.) The ForEach code block can contain one or many statements seperated by semicolons. The last object returned in the code block is forwarded into the pipeline.

   Get-Service | ForEach { $parent = $_.Name; $_.DependentServices } |
                         Select {$parent}, Status, Name

This collects one or more pieces of data from the parent object (the Service) as variables, and then passes the DependentServices object on through the pipeline. On the other side of the pipeline you can continute expanding objects and using the variables as needed. Here’s the Service and DependentServices solution using “Store and Forward”.

image


Using ForEach with Custom Objects

You can customize objects by adding “NoteProperties” to the object. You saw one form of this when we used the -ExpandProperty while listing other properties.

   image


As a fun example, lets create a custom object that looks like a Service object, but has two properties named RunningDependentProperties and NonRunningDependentProperties.

We will need:

  • A variable for the parent Service
  • Two variables to store the child services (Empty arrays created as $r=@().)
  • The Add-Member cmdlet to add a new NoteProperty to the Service object

Get-Service |
     foreach {
       # initialize the arrays
       $r = @();
       $nr = @();
       # process all of the DependentServices
       ($_.DependentServices |
          foreach { if ($_.status -eq "Running") { $r += $_ } else {$nr += $_ } }
       );  `
       # attach the results to the original service
       Add-Member -InputObject $_ -name RunningDependentServices -Value $r -MemberType NoteProperty;
       Add-Member -InputObject $_ -name NonRunningDependentServices -Value $nr -MemberType NoteProperty;
       $_
     } |
Select name, RunningDependentServices, NonRunningDependentServices


So what’s it good for?

How about a list of only services with running dependent services? Just add a WHERE before the SELECT:

    where { $_.RunningDependentServices.Count -gt 0 }

   image

Or, how about a list of all of the services where the DependentService “applockerfltr” is not running? Just add a WHERE before the SELECT:

   where { "applockerfltr" -in $_.NonRunningDependentServices.Name }


(If you are not reading this at http://TechTrainingNotes.blogspot.com… you are reading stolen content!)


Tip:

In addition to the expansion of properties that are collections, -ExpandProperty can also be used to return a simple datatype like a string or a number. By default, a Select returns a collection. In the example below note that a basic Select Status returns a property named “Status” that contains the value “Running”. If you pipe that to Get-Member you will see that it is still a ServiceController object. If you expand the property Status you will then get the simple string value of the property.

image


.

No comments:

Note to spammers!

Spammers, don't waste your time... all posts are moderated. If your comment includes unrelated links, is advertising, or just pure spam, it will never be seen.