A proper title for this post could have been "What is PowerShell?" The PSObject layer and the Extended and Adapted Type Systems are the heart of what make PowerShell what it is - a shell for all objects. If you are one of the many who confuse PowerShell as a .NET shell, by the end of this post you will recognize how in fact PowerShell serves the much broader role of a shell for all object frameworks. Many different kinds of objects are presented as first-class citizens in the PowerShell environment, including COM, ADO.NET, WMI/CIM, and XML objects, as well as .NET ones. The mechanism which makes this possible is the PSObject layer and the ATS and ETS.

Objects and Members

Before explaining the PSObject layer, we must explain a key concept regarding objects. All objects comprise a) data and b) ways of interacting with and via that data. The developer of the object concerns him or herself with arrangement and storage of the data (part a); users of the object (such as PowerShell users) concern themselves only with the interfaces the developer provides for interacting with that data (part b). These points of interaction are collectively known as the members of the object. Types of members in .NET include Properties, Methods, Fields, and Events. In PowerShell, additional member types such as NoteProperties, CodeMethods, and MemberSets are available. Because all forms of interaction with an object are via its members, from the perspective of the user an object is essentially synonymous with the members it exposes. That is, an object is a collection of members.

This perspective of objects as a collection of members is at the heart of PowerShell's type systems. Components of PSObject work together to identify and retrieve an object's members from various sources and PowerShell surfaces all members from all these sources uniformly. Whether the member comes from an attribute of an XML node, from a COM server interface, from a column in an ADO.NET row, from a .NET property, or even from the PowerShell type system itself, it looks the same to the PowerShell end user.

PowerShell achieves this unified projection of all members via the PSObject layer and its constituents. Instead of surfacing a WMI object, COM object or .NET object (for example) exactly as they would be surfaced in other .NET applications, PowerShell wraps the underlying object in a PSObject. This wrapper layer interacts with the underlying object and exposes additional members from it and for it not otherwise available in .NET. It does this via the Extended and Adapted Type Systems (ETS and ATS).

The Adapted Type System (ATS)

We begin with PowerShell's type adaptation system. In this system, PowerShell checks the true framework which an object is part of (e.g. COM, WMI, etc.) and accordingly adapts attributes and methods from the underlying framework into first-class members of the PowerShell object. Let us use WMI objects as an example. The System.Management.ManagementObject class provides a wrapper in .NET for WMI objects. Whatever the underlying WMI type of these objects, they all appear the same in .NET, with the same direct properties and methods available (as documented in the linked MSDN page). In PowerShell, the type adaptation system elevates the properties and methods of the underlying WMI object into direct, first-class members of the PowerShell WMI object. For example, the Terminate() method of a Win32_Process WMI object cannot be called directly in a standard .NET application, but can be called in PowerShell.

To illustrate the difference between the unadapted (base) set of members and PowerShell's adapted set of members for a WMI object, run the following commands:

PS:> Get-WMIObject Win32_Process | Get-Member -View Base # Members of underlying .NET object only
PS:> Get-WMIObject Win32_Process | Get-Member -View Adapted # Members of object as adapted by PowerShell

The first command returns a view of the members of the object as they would appear in a .NET application. The second command returns a view of the members exposed by default in PowerShell - an adapted view of the object.

WMI is not the only framework whose members are adapted into first-class members in PowerShell. The other out-of-the-box adapters include:

Object Framework

.NET Base Class

WMI Class

System.Management.ManagementClass

WMI Object

System.Management.ManagementObject

ADSI Object

System.DirectoryServices.DirectoryEntry

ADO.NET DataRowView

System.Data.DataRowView

ADO.NET DataRow

System.Data.DataRow

XML

System.Xml.XmlNode

PSObject

System.Management.Automation.PSObject

PSMemberSet

System.Management.Automation.PSMemberSet

COM Object

System.__ComObject

.NET Object

System.Object

Any object deriving directly or indirectly from one of the base classes on the right is automatically adapted into a first-class PowerShell object. In addition, it is possible for third parties to write limited adapters of their own by creating an adapter class deriving from PSPropertyAdapter and associating this custom type adapter with a base .NET class via a TypeAdapter node in a types.ps1xml file or via Update-TypeData.

The Extended Type System (ETS)

Type adaptation allows members from alternate object frameworks such as COM and WMI to be surfaced as first-class members in PowerShell. Type extension allows additional dynamic members to be added to all types and objects in PowerShell. For example, the Mode column and property displayed for file and directory objects is created via PowerShell's ETS, as is the EventID property of an EventLog entry. CPU is one of several custom properties added to Process objects.

Extended members can be added to PowerShell objects arriving from any object framework, including PSObjects. They can be added to individual instances via the Add-Member cmdlet, or to all instances of a class via a types.ps1xml file or the Update-TypeData cmdlet. To retrieve only the extended members of a PowerShell object, utilize the following:

PS:> Get-WmiObject Win32_Process | Get-Member -View Extended # Members added by PowerShell

In fact, many objects in PowerShell are simply empty PSObjects with extended members. For example, the only significant members of objects created via New-Object -TypeName PSObject are extended members. Import-Csv creates custom PSObjects with extended properties created from the rows of the underlying file; Select-Object works similarly with properties of an input object. Some cmdlets, such as SharePoint's Get-SPFarmConfig, return PSObjects representing configuration details solely as extended properties.

To summarize, the Adapted Type System is the means by which PowerShell adapts objects from frameworks other than .NET into first-class objects in PowerShell. The Extended Type System is the means by which PowerShell dynamically adds members to objects. Both of these mechanisms are enabled by the PSObject layer.

PSObject

The MSDN documentation regarding PSObject states that it "allows for a consistent view of any object within the Windows PowerShell environment." We've discussed the Base members of a PowerShell object, its Adapted members, and its Extended members, obtaining them by specifying specific views for Get-Member's -View parameter. Let us now discuss and view the members of the PSObject layer itself.

The PSObject layer is almost entirely transparent within PowerShell. To retrieve a view of this layer, call the psobject MemberSet member from any object in PowerShell. In an interesting twist, MemberSets are adapted into first-class objects in PowerShell (as indicated in the table above) and the direct members of PSObject are listed. For example, try the following:

PS:> $proc = Get-Process explorer
PS:> $proc.psobject

Of the members returned, the Members property includes the collection of members of this object from all sources - base, adapted, and extended. The BaseObject property refers to the base .NET object from which the PowerShell object has been adapted and extended. It is these key properties which PowerShell refers to when handling interactions with the object and its members.

In conclusion, it should now be clear that PowerShell is not at all only a .NET shell. Instead, it has been designed with deep consideration for out-of-the-box and customizable interaction with all kinds of objects and object frameworks.