Extending the CimProcess Object
In this issue:
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