Hate Add-Member? (PowerShell’s Adaptive Type System to the Rescue)

Do you hate Add-Member as much as I do?

Wait – maybe you aren’t familiar with Add-Member or the glory of PowerShell’s Adaptive Type System.  (ATS).  When I looked at the .NET type system, my reaction was “….almost”.  I’m not trying to throw a rock at .NET – anyone that knows me knows how much I love .NET but the reality is that it’s type system “almost” meets the needs of a management system … but doesn’t.  That is why we invented the Adaptive Type System. 

There are 4 main components of ATS:

Type Adapters. 

A lot of technologies implement their own type systems within .NET.  Most of these technologies are critical to Management scenarios (WMI, ADSI, XML, ADO, etc)  so we needed to do something about this.  What I mean by “implement their own type system” is that they have a small number of .NET types which then map to zillions of their own types.  There are a zillion WMI objects but they are all represented by a 2 or 3 .NET classes.  Type adapters allow us to adapt those technologies so they look like what users what out of a type – namely it’s properties and methods.  A normal WMIObject would look like this:

Scope            : System.Management.ManagementScope
Path             :
\\JPSLAP14\root\cimv2:Win32_Service.Name=”AeLookupSvc
Options          : System.Management.ObjectGetOptions
ClassPath        :
\\JPSLAP14\root\cimv2:Win32_Service
Properties       : {AcceptPause, AcceptStop, Caption, CheckPoint…}
SystemProperties : {__GENUS, __CLASS, __SUPERCLASS, __DYNASTY…}
Qualifiers       : {dynamic, Locale, provider, UUID}
Site             :
Container        :

We adapt that object so it looks like this:

ExitCode  : 0
Name      : AeLookupSvc
ProcessId : 1068
StartMode : Auto
State     : Running
Status    : OK

Extended Metadata System

.NET gives you a meat-and-potatoes type system giving you properties, fields, methods, interfaces and events.  That is awesome but in management scenarios, we need more.  PowerShell extends the typesystem with PropertySets, Aliases, MemberSets and then provides richer set of Properties and methods (e.g. Script Properties, CodeProperties, ParameterizedProperties, etc).

Type MashUps

One of the most powerful and least used aspects of PowerShell is it’s type mashup system. What this does is allow anyone to extend the type system with their own properties, methods, metadata, etc.  You can extend the types or the instances of the types.  This is CRAZY useful when you have one command which outputs objects with a property called SERVER and you want to pipeline it to a command which accepts any object in the pipeline that has a property called COMPUTERNAME.  All you have to do is to modify that definition of that type to ALIAS the property COMPUTERNAME to SERVER and now the pipelining happens with no additional code. This is freakishly powerful stuff.

Dynamic Types

In addition to extending a TYPE, you can dynamically extend any instance of a type.  Take ANY object.  You can add ANY additional property, alias, method, etc to THAT instance of the object and it won’t affect any other instance.  I find that I want to do this all the time.  The mechanism for doing this is Add-Member.  Here is what Add-Member looks like:

Adds a user-defined custom member to an instance of a Windows PowerShell object.
Add-Member
        (0)-MemberType | -Type <AliasProperty | All | CodeMethod | CodeProperty | Event | MemberSet | Method |
                                             Methods | NoteProperty | Parameterty | Properties | Property | PropertySet | 
                                             ScriptMethod | ScriptProperty>
        (1)-Name <String>
           -InputObject <PSObject> (ByValue)
        (2)[-Value <Object>]
        (3)[-SecondValue <Object>]
           [-Force ]
           [-PassThru ]

Here is an example of it in use:

PS>$l = gps lsass
PS>Add-Member -InputObject $l -MemberType NoteProperty -Name test -value “PowerShell ROCKS!”
PS>$l.test
PowerShell ROCKS!
PS>$l |fl t*

test               : PowerShell ROCKS!
Threads            : {684, 688, 692, 700…}
TotalProcessorTime : 00:01:48.9354983

SOOOO – why do I hate Add-Member?

I love that Add-Member lets me do a zillion things but in reality, 95% of the time, I just want to add a bunch of NOTEPROPERTIES to an object and Add-Member feels way to heavy to accomplish that.  I’ve complained about this a number of times and tried to shame Bruce into fixing it but “to ship is to choose” and it has not popped up as the next best use of available calories.  After trying and failing once again, I realized that I was being stupid – that ATS allowed me to control my own destiny. 

What I decided to do was to add a new script method PSAddMember() to EVERY object in the system by attaching it to System.Object.  This method would have 3 signatures. It would take a name/value pair, it would take a name/value/membertype tuple and it would take a hashtable which is a set of name/value pairs.  Here is the file that does that and a demonstration of it in action:

<?xml version=”1.0″ encoding=”utf-8″ ?>
<Types>
    <Type>
        <Name>System.Object</Name>
        <Members>
            <ScriptMethod>
                <Name>PSAddMember</Name>
                <Script>
switch ($args.count)
{
1   {
    $hash = $args[0] -as [HashTable]
    foreach ($key in $hash.keys)
    {
        Add-Member -InputObject $this -Name $key -value $hash.$key -MemberType Noteproperty -Force
    }
    }

2   {
    $name,$value = $args
    Add-Member -InputObject $this -Name $name -value $value -MemberType Noteproperty -Force
    }

3   {
    $name,$value,$MemberType = $args
    Add-Member -InputObject $this -Name $name -value $value -MemberType $MemberType -Force
    }
}
                </Script>
            </ScriptMethod>
        </Members>
    </Type>
</Types>

—————————————————————-

PS> $p = gps lsass
PS> $p.PSAddMember(“q1″,”Value1″)
PS> $p.PSAddMember(“q3″,{“*”* $this.Threads.Count},”ScriptProperty”)
PS> $p.PSAddMember(“q2″,{“*”* $this.Threads.Count},”ScriptProperty”)
PS> $hash = @{q3=4; q4=”Q4″; q5=(gsv alg)}
PS> $p.PSAddMember($hash)
PS> $p

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
——-  ——    —–      —– —–   ——     — ———–
   1721      19    12688      19756    70   110.45    652 lsass

PS> $p.q1
Value1
PS> $p.q2
*****************************
PS> $p |fl q*

q1 : Value1
q2 : *****************************
q4 : Q4
q5 : System.ServiceProcess.ServiceController
q3 : 4

 

PS> $p

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
——-  ——    —–      —– —–   ——     — ———–
   1721      19    12688      19756    70   110.45    652 lsass

Lessons learned/relearned:

  1. PowerShell’s Adaptive Type System (ATS) is awesome.
  2. With ATS you are in control of your own destiny.  (If you don’t like the world – change it yourself. [then share to help others])

Enjoy!

Jeffrey Snover [MSFT]
Windows Management Partner Architect
Visit the Windows PowerShell Team blog at:    http://blogs.msdn.com/PowerShell
Visit the Windows PowerShell ScriptCenter at:  http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx