Weekend Scripter: Clean Up Your WMI Data Output in PowerShell

Summary: Microsoft Scripting Guy, Ed Wilson, shows you how to use Windows PowerShell to clean up your WMI data output.

Microsoft Scripting Guy, Ed Wilson, is here. So far the Scripting Wife has been scarce. She got up before sunrise (I believe) and headed out to points unknown. Actually, I believe she had scheduled a “chicks breakfast” with her friends who are hanging out down here in Myrtle Beach, South Carolina (and no, it was not a PowerShell Chicks User Group breakfast). But that is OK, because it means that I get to fend for myself. And with a beach, ocean, and tons of cool things to do within walking distance, fending for one’s self was never easier. There is even wifi on the beach—the only trick is to avoid getting sand in one’s laptop.

The problem with extra “stuff” in a WMI query

The problem of getting “extra stuff” back when you do a WMI data query has been around since Windows PowerShell 1.0 (even longer if you count the beta period). But to be honest, it is not that you get extra stuff back when you perform a WMI query, it is that certain WMI classes have a default output that is configured via the types.ps1xml file. For example, the image that follows illustrates the portion that shows the format XML information for the Win32_BIOS WMI class.

Image of command output

When you perform a basic WMI query, the default display formats the output. This is because the basic query (no specifically requested properties) matches the System.Management.ManagementObject#root\cimv2\Win32_BIOS type that is specified in the format XML file. This information is shown here:

PS C:\> gwmi win32_bios | gm | select typename -Unique





When I use the Property parameter or I perform a custom WQL query, the returned type changes; and therefore, it does not match the type that is defined in the types.ps1xml file, as shown here.

PS C:\> gwmi win32_bios -property name | gm | select typename -Unique






PS C:\> gwmi -q “select name from win32_bios” | gm | select typename -u





Filtering out the system properties

So what is the “extra stuff” that comes back when a custom WMI query runs? They are system properties. One of the nice things about WMI is that all of the system properties begin with a double underscore ( __ ). In addition, all system WMI classes begin with a double underscore. When you run a WMI query that selects only the name property, a whole bunch of system properties also return. This is shown here:

PS C:\> gwmi -q “select name from win32_bios”


__GENUS          : 2

__CLASS          : Win32_BIOS


__DYNASTY        :

__RELPATH        :


__DERIVATION     : {}

__SERVER         :

__NAMESPACE      :

__PATH           :

Name             : Default System BIOS

One way to clean up the output is to filter these properties by using a range in either Format-Table or Format-List as shown here:

PS C:\> gwmi -q “select name from win32_bios” | fl [a-zA-Z]*


Name             : Default System BIOS

Scope            : System.Management.ManagementScope

Path             :

Options          : System.Management.ObjectGetOptions

ClassPath        : Win32_BIOS

Properties       : {Name}

SystemProperties : {__GENUS, __CLASS, __SUPERCLASS, __DYNASTY…}

Qualifiers       : {dynamic, Locale, provider, UUID}

Site             :

Container        :


PS C:\> gwmi -q “select name from win32_bios” | ft [a-zA-Z]*


Name     Scope    Path     Options  ClassPat Propert SystemP Qualifi Site    Contain

                                    h        ies     roperti ers             er


—-     —–    —-     ——-  ——– ——- ——- ——- —-    ——-

Defau… Syste…          Syste… Win32… {Name}  {__G… {dyn…

Unfortunately, in Windows PowerShell 2.0, that filter trick quit working because no sooner do you get rid of one group of system properties, than another group of system properties appears. It does not matter, whether you use the Format-Table cmdlet or if you use the Format-List cmdlet—the extra system properties appear to be here to stay.

Using Format-Table or Format-List

If you specifically choose the properties in your WMI query, you also need to specifically choose them in the Format-Table or Format-List query. This technique is shown here:

PS C:\> gwmi -Property name -Class win32_bios | ft name




Default System BIOS


PS C:\> gwmi -Property name -Class win32_bios | fl name


name : Default System BIOS

There are at least two things “wrong” with this approach. The first is that once you use a Format-Table, Format-List, or Format-Wide cmdlet, you cannot do anything else with your pipeline because the object is now gone. In other words, the format* cmdlets destroy the pipeline. What is the difference?  Well, it can be seen by examining the object in the pipeline.

In the first example, we see that we have a win32_bios management object.

PS C:\> gwmi -Property name -Class win32_bios | gm | select typename -u





But following the pipeline to the Format-List cmdlet, we no longer have a management object at all. Instead they are all format-related objects as shown here.

PS C:\> gwmi -Property name -Class win32_bios | fl name | gm | select typename –









Remove the “extra stuff” and retain the object

The best way to remove the “extra stuff” is to use the Select-Object cmdlet. This cmdlet removes the “stuff” and retains the object-oriented nature of the data. This means that you can do other things with it. In the command that follows, the Name and the ProcessID properties from the Win32_Service WMI class are chosen from the WMI data. Next, the Select-Object cmdlet chooses the same properties. Now they are sent to Get-Member, and the typename is displayed. This is shown here:

PS C:\> gwmi -Property name, processID -Class win32_Service | Select name, processID

| gm | select typename -u





Now, what does this look like when it is displayed to the Windows PowerShell console? The following is a partial output (note that there are no system properties).

PS C:\> gwmi -Property name, processID -Class win32_Service | Select name, processID


name                                                                       processID

—-                                                                       ———

AdobeActiveFileMonitor6.0                                                       2036

AdobeARMservice                                                                 1776

AeLookupSvc                                                                        0

ALG                                                                                0

AppIDSvc                                                                           0

Appinfo                                                                          504

AppMgmt                                                                            0

aspnet_state                                                                       0

AudioEndpointBuilder                                                             456

AudioSrv                                                                         336

AxInstSV                                                                           0

BDESVC                                                                             0

BFE                                                                             1920

BITS                                                                             504

Browser                                                                            0

bthserv                                                                         2796

But what about the object itself? Look at what Get-Member displays. We have created a custom management object with two properties: Name and ProcessID.

PS C:\> gwmi -Property name, processID -Class win32_Service | Select name, processId

| gm


   TypeName: Selected.System.Management.ManagementObject


Name        MemberType   Definition

—-        ———-   ———-

Equals      Method       bool Equals(System.Object obj)

GetHashCode Method       int GetHashCode()

GetType     Method       type GetType()

ToString    Method       string ToString()

name        NoteProperty System.String name=AdobeActiveFileMonitor6.0

processId   NoteProperty System.UInt32 processId=2036

Because this is still a management object, we can continue to use Windows PowerShell to massage the data. As shown here, we can use the Sort-Object cmdlet to sort the data, and we can use the –Last parameter to choose the last two services.

PS C:\> gwmi -Property name, processID -Class win32_Service | sort name | Select name

, processId -Last 2


name                                                                       processId

—-                                                                       ———

wudfsvc                                                                          456

WwanSvc                                                                            0

One thing that can simplify things is to list the properties that you are interested in obtaining in an array, and then use them directly in your Get-WmiObject and your Select-Object queries. In this way, you only need to type them once. Because I do not like typing quotation marks, I create a single string with the CSV list of property names. Then I use the Split operator to create my array. The thing to keep in mind is to not put spaces between the property names. This code is shown here:

$property = “Name,started,StartName” -split “,”

gwmi -p $property -cl win32_service | select $property

When you have the properties in an array, you can use them as often as you want without the need to retype them. In the example that follows, the Format-Table cmdlet is used to display a table of service information.

$property = “Name,started,StartName” -split “,”

gwmi -p $property -cl win32_service | select $property | ft $property –AutoSize

The use of the commands and their associated output is shown here:

Image of command output

Well, that is about it for right now. The Scripting Wife will be back soon (I imagine), and then she was talking about going to do something. No idea what—just something. But hey, we are at the beach, so there is bound to be something going on. Maybe we can found the Myrtle Beach Windows PowerShell Users Group while we are down here.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy