Exploring PowerShell Objects: Flattening

Hidden Treasure

Knowing how to learn and explore in PowerShell is very important.  Jeffrey Snover and other experts often mention that they might not memorize exactly what to run, but they know how to use tools for discovery and exploration built into PowerShell and the .NET Framework.  Tools like Get-Command, Get-Help, Get-Member, and Select-Object go a long way.

Two of PowerShell’s greatest features can make it somewhat tedious when first working with and exploring a particular technology.

  • PowerShell can automate and control a wide variety of technologies
  • PowerShell is an object based language

Let’s look at an example.  Pretend you are working with a process object for the first time!

Get-Process -name PowerShell*

image

Not much info, is there?  What if I want the path to the process?  Details on module file names?  I would need to use Get-Member and Select-Object for this, or perhaps Lee Holmes’ excellent Show-Object.

Whichever path, none of these provide a quick outlook on all the properties and values of an object.

The Problem

Hopefully, none of us are stuck solely working with process objects. Maybe you work with the various technologies PowerShell can hit; .NET libraries, numerous RESTful or other web APIs, each of which might expose a variety of different objects. Exploring the data and schema behind these is very helpful; maybe you can use it in building a solution, or to help learn and pick up a new skillset.

Show-Object is a great way to explore these – it takes an object and breaks it down into a tree that you can explore. Nine times out of ten, this will do the trick. But what if you want to include or exclude specific nodes?  Search for a particular value of a node? Exclude the 20+ default properties that make exploring XML so painful? Here’s Show-Object with an empty XML document:

ShowObject

Wouldn’t it be nice to explore objects like this without the unnecessary properties?

Flattening

ConvertTo-FlatObject is a function that neatly fits these scenarios.  Rather than presenting a tree, it attempts to flatten the entire object, leaving you a usable object as output.  Let’s start with a concocted example.

$Object = New-Object -TypeName PSObject -Property @{            
    "A-1" = 1            
    B = $(Get-Date)            
    C = @{'HashTableKey!' = "val"}            
    D = @(1..2 | ForEach-Object {            
        New-Object -TypeName PSObject -Property @{            
            $_ = "a$_"            
        }            
    })            
}             
            
$Object | ConvertTo-FlatObject

FlatObject1

ConvertTo-FlatObject won’t handle everything, but it will recursively list out the properties of an object, including hashtables, arrays, and paths with special characters. The output is formatted so that you can copy and paste properties for future use:

FlatObject2

Who would design such a silly schema?  As you spend time with code, it becomes clear that complexity is the norm.

Practical Examples!

Commvault

Let’s play with a real example.  Commvault Simpana provides a RESTful API.  We’ll use slightly modified XML from here.  Perhaps we want to take this mess of XML and expand out any leaf that ends in “ID”, and we don’t care about common properties:

$Object = [xml]'<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
 <subClientProperties>
 <commonProperties enableBackup="true" description="compliance" encryptionFlag="ENC_NETWORK_AND_MEDIA" fsFLRReplicationOptions="540672" isKeepItemPastExpiryTime="true" numberOfBackupStreams="2" readBuffersize="256">
 <prepostProcess runPostBackup="NO"/>
 <snapCopyInfo>
 <snapToTapeProxyHost _type_="CLIENT_ENTITY" clientId="2"/>
 </snapCopyInfo>
 <storageDevice applicableReadSize="4096" networkAgents="2" softwareCompression="USE_STORAGE_POLICY_SETTINGS" throttleNetworkBandwidth="-1">
 <dataBackupStoragePolicy _type_="STORAGE_POLICY_ENTITY" storagePolicyId="7"/>
 <deDuplicationOptions enableDeduplication="true" generateSignature="ON_CLIENT"/>
 </storageDevice>
 </commonProperties>
 <content path="C:\\DOC_folder"/>
 <fsSubClientProp backupSystemState="false" backupSystemStateforFullBkpOnly="false" retentionRule="IMMEDIATELY" useGlobalFilters="OFF" useVSS="true"/>
 <impersonateUser/>
 <subClientEntity _type_="SUBCLIENT_ENTITY" applicationId="33" backupsetId="3" clientId="2" instanceId="1" subclientId="6"/>
 </subClientProperties>'            
            
$Object | ConvertTo-FlatObject -include *ID -Exclude commonProperties

FlatObject3

Rather than exploring each Commvault object I care about by hand, I can now quickly get a feel for the data and schema behind them.

StackOverflow

StackExchange has a nice public API we can hit with Invoke-RestMethod.  What data do we get back?

$Uri = "https://api.stackexchange.com/2.0/questions/unanswered?"
$Uri += "order=desc&sort=activity&tagged=powershell&pagesize=2&site=stackoverflow"            
            
Invoke-RestMethod -Uri $Uri | ConvertTo-FlatObject

FlatObject4

Active Directory

Sometimes I know the value of what I want, but not the property name.  Sure, I could manually skim through all the properties.  But this is slow, and I have bad eyes.

I know I have an alternate e-mail address… where is that stored again?

Get-ADUser REDACTED -Properties * |            
    ConvertTo-FlatObject -Value "*cookie.monster*" -MaxDepth 1

FlatObject5

On a side note, that MaxDepth 1 saves a bit of time if you know you only want to see the first layer of properties.

Under the Hood

So what are we using to flatten these objects, ignoring a non-developer’s sad attempt at recursion?

Reflection

PowerShell uses the .NET Framework.  This means we have access to System.Reflection.Assembly.  One handy method from this class is GetType.

Refl

You can run this on any object in PowerShell.  So, how does this come up in ConvertTo-FlatObject?  Here’s an illustration with XML, one of the main reasons I use this method:

Refl2

In ConvertTo-FlatObject, we use GetType to get the type we are working with, and then GetProperties to find the default properties that we may want to ignore.

PSObject

You might have read various posts on inserting a type name into a custom object.  These use the hidden property PSObject, which has a handy Properties property, allowing for ordered extraction of properties and their values.

Here’s a quick example getting properties of a DirectoryInfo object:

image

In ConvertTo-FlatObject, we use this to extract property names and values.

Back to Basics

Don’t forget the basics!  Tools like Show-Object and ConvertTo-FlatObject are convenient, but you should still spend time getting to know how to learn and explore in PowerShell.

I can’t imagine a day going by where I don’t depend on Get-Command, Get-Help, Get-Member and Select-Object.  Get very familiar with these Cmdlets!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s