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*
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:
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
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:
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
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
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
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.
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:
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:
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!