Behind the PowerShell Pipeline logo

Behind the PowerShell Pipeline

Archives
Log in
Subscribe
September 4, 2026

Extending the CimProcess Object

In this issue:

  • Extending the Object Type
    • Inserting a TypeName
    • Aliases
    • ScriptProperties
  • Default Property Set
  • Types.ps1xml
  • Summary

Let's continue to explore what we can learn from my solution to last month's PowerShell scripting challenge. Last time I shared my updated function that uses Get-CimInstance to query a computer for Win32_Process instances as a replacement for Get-Process. We can use my function as a model for your scripting projects.

When creating PowerShell functions, you should always look at the output and ask yourself what would make it more efficient? What value could you add? What could you do to the output to make it easier for the user? The user might be you but it doesn't have to be. How do you envision your command being used? PowerShell commands are generally not executed in a vacuum. Your command will most likely be used in a PowerShell expression to achieve some result. What can you do to make your command easy to use in pipelined expression and how can you optimize the output? I am trying to get you to think beyond simply running a command like Get-CimInstance in your function and assuming if it works, you're good. I want you to elevate your code.

Extending the Object Type

What I'm talking about is extending an object's type. I'll eventually get to formatting. My function writes a Win32_Process CIMInstance to the pipeline:

PS C:\> Get-Win32Process -id $pid | Tee-Object -Variable p

ProcessId Name     HandleCount WorkingSetSize VirtualSize
--------- ----     ----------- -------------- -----------
11040     pwsh.exe 900         153104384      2341338222592

PS C:\> PS C:\> $p.GetType().fullName
Microsoft.Management.Infrastructure.CimInstance

The typename is everything as you can only extend and format objects with a defined typename. A generic PSCustomObject won't cut it.

In PowerShell, the type system relies on .NET where object classes can inherit type names which can lead to a hierarchy of type names. The type name refers to the object's class definition. Use the implicit PSObject property to view the type name hierarchy.

PS C:\> $p.PSObject.TypeNames
cimProcess
Microsoft.Management.Infrastructure.CimInstance#root/cimv2/Win32_Process
Microsoft.Management.Infrastructure.CimInstance#ROOT/cimv2/CIM_Process
Microsoft.Management.Infrastructure.CimInstance#ROOT/cimv2/CIM_LogicalElement
Microsoft.Management.Infrastructure.CimInstance#ROOT/cimv2/CIM_ManagedSystemElement
Microsoft.Management.Infrastructure.CimInstance#Win32_Process
Microsoft.Management.Infrastructure.CimInstance#CIM_Process
Microsoft.Management.Infrastructure.CimInstance#CIM_LogicalElement
Microsoft.Management.Infrastructure.CimInstance#CIM_ManagedSystemElement
Microsoft.Management.Infrastructure.CimInstance
System.Object

The first name is what you would see when piping the object to Get-Member. I inserted the cimProcess, which I'll explain in a moment. I haven't defined any type extensions or formatting for this type so PowerShell uses the default formatting based on the next type in the list.

Inserting a TypeName

I could have extended the Win32_Process type, but that would then apply to any command that emitted that type. I could have created a PSCustomObject in my code and given that a unique typename, but I wanted to keep all properties of the Win32_Process class since I don't know information someone might want.

Instead, I am inserting a new typename into the objects before they are written to the pipeline.

$get = Get-CimInstance @cimParams
# ...
$get.Foreach({ $_.PSObject.TypeNames.Insert(0, 'cimProcess') })
$get

I am inserting the typename cimProcess into the first position and then writing the array of objects to the pipeline.

For instances that include the user name, that will be a slightly different type name.

$get.Foreach({
    #insert a new type name
    $_.PSObject.TypeNames.Insert(0, 'cimProcess#Username')
    #add a property for the user
    $user = Invoke-Command $getUser -ArgumentList $_
    $_ | Add-Member -MemberType NoteProperty -Name Username -Value $user -Force -PassThru
})

I'm making a slight change to the original object so I am using a variation on the new typename to indicate what makes it special. In this case, the username.

At the beginning of the function I can document this information.

[OutputType('cimProcess', 'cimProcess#Username')]

This attribute doesn't have any effect on the code. PowerShell only uses it for documentation purposes. Normally, PowerShell functions should only write one type of object to the pipeline. I am bending the rule a little bit. The two types are nearly identical except that one has an extra property name.

Aliases

An easy step in evaluating your output is to ask, "What would make it easier to use?" Right now, I need to know the exact property names to use in an expression like this:

PS C:\> $p | Select ProcessID,Name,WorkingSetSize,CreationDate,CSName

ProcessID      : 11040
Name           : pwsh.exe
WorkingSetSize : 153104384
CreationDate   : 8/28/2026 8:27:32 AM
CSName         : CADENZA

Part of the challenge was to make the output similar to Get-Process.

PS C:\> ps -id $pid | Select ID,Name,WorkingSet,StartTime,MachineName

Id          : 11040
Name        : pwsh
WorkingSet  : 167235584
StartTime   : 8/28/2026 8:27:32 AM
MachineName : .

Instead of forcing the user to know and remember CSName, it is much easier to use the commonly used property Computername. If I add a property alias, I'll reduce friction a little bit. The easy way to accomplish this is with Update-TypeData.

Update-TypeData -TypeName cimProcess -MemberType AliasProperty -MemberName Computername -Value CSName -Force

This is why you need a unique typename. I can create aliases for other properties as well. And you aren't limited to one alias per property.

Update-TypeData -TypeName cimProcess -MemberType AliasProperty -MemberName ID -Value ProcessID -Force
Update-TypeData -TypeName cimProcess -MemberType AliasProperty -MemberName StartTime -Value CreationDate -Force
Update-TypeData -TypeName cimProcess -MemberType AliasProperty -MemberName WorkingSet -Value WorkingSetSize -Force

Keep in mind that because of inheritance, there may already be type extensions for a parent type.

PS C:\> Get-CimInstance win32_process -filter "ProcessID=$pid" | Get-Member

   TypeName: Microsoft.Management.Infrastructure.CimInstance#root/cimv2/Win32_Process

Name                       MemberType     Definition
----                       ----------     ----------
Handles                    AliasProperty  Handles = Handlecount
ProcessName                AliasProperty  ProcessName = Name
VM                         AliasProperty  VM = VirtualSize
WS                         AliasProperty  WS = WorkingSetSize
...

When I pipe a cimProcess object to Get-Member I'll see these extensions as well.

Now I can use these aliases in an easy to use and read expression:

PS C:\> $p | Select ID,Name,WS,StartTime,Computername

ID           : 11040
Name         : pwsh.exe
WS           : 153104384
StartTime    : 8/28/2026 8:27:32 AM
Computername : CADENZA
Want to read the full issue?
Already a paid subscriber? Click here to log in.
GitHub
Bluesky
LinkedIn
Mastodon
jdhitsolutions.github.io
Powered by Buttondown, the easiest way to start and grow your newsletter.