Supporting Additional View Details
A question came up on an internal discussion list today about how to let a cmdlet support both simple and detailed views. For example, a –Detailed flag to tell the cmdlet to emit additional information during the request.
The original approach was to do all of this filtering inside of the cmdlet. When –Details is not specified, run through some additional code to remove the extraneous properties.
The goal is noble, but the implementation decision is misguided.
The correct approach here is to have your cmdlet ALWAYS output all of the information. Your default display should have the most important properties, but users shouldn’t have to specify the –Details switch to get access to more.
Compare to System.Diagnostics.Process, a property-rich class emitted by the Get-Process command:
PS C:\Windows\System32> $powershell = Get-Process -Id $pid
PS C:\Windows\System32> $powershell
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
445 9 36688 38156 175 0.78 9828 powershell
If the user wants to view additional details:
PS C:\Windows\System32> $powershell | format-list *
__NounName : Process
Name : powershell
Handles : 445
VM : 183631872
WS : 39071744
PM : 37568512
NPM : 9500
(...)
But they can still access specific details if they want:
PS C:\Windows\System32> $powershell.FileVersion
6.1.6930.0 (fbl_srv_powershell(leeholm).071018-1329)
If you have different views of the information you want to surface, our Formatting and Output system fully supports this through formatting extensions and View Names.
In some cases, different views really are a core scenario for the cmdlet. For example, WMI returns an enormous amount of data for nearly everything you request. However, you care about different properties for processes than you do for services, logical disks, etc. In that case, you can add a synthetic type name to the objects you return that indicate the view. Then, write types or formatting sections that apply directly to the synthetic type:
PS >$disk = (Get-WmiObject Win32_LogicalDisk)[0]
PS >$disk.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True ManagementObject System.Management.ManagementBaseObject
PS >$disk.PsObject.TypeNames
System.Management.ManagementObject#root\cimv2\Win32_LogicalDisk
System.Management.ManagementObject
System.Management.ManagementBaseObject
System.ComponentModel.Component
System.MarshalByRefObject
System.Object
and the corresponding type definition:
<Type>
<Name>System.Management.ManagementObject#root\cimv2\Win32_LogicalDisk</Name>
<Members>
<PropertySet>
<Name>PSStatus</Name>
<ReferencedProperties>
<Name>Status</Name>
<Name>Availability</Name>
<Name>DeviceID</Name>
<Name>StatusInfo</Name>
</ReferencedProperties>
</PropertySet>
<MemberSet>
<Name>PSStandardMembers</Name>
<Members>
<PropertySet>
<Name>DefaultDisplayPropertySet</Name>
<ReferencedProperties>
<Name>DeviceID</Name>
<Name>DriveType</Name>
<Name>ProviderName</Name>
<Name>FreeSpace</Name>
<Name>Size</Name>
<Name>VolumeName</Name>
</ReferencedProperties>
</PropertySet>
</Members>
</MemberSet>
</Members>
</Type>
Use the PsObject.TypeNames.Add()
method to add custom type names to your output object.
The core tenet here is minimizing the amount of back-tracking the user needs to do. When they realize they need additional data, don’t make them re-run the command, specify –Details, and start over. If your cmdlet always emits the information (even if it is hidden by a view,) they can still access it when they want.