Is Add-Member the most underrated and underappreciated cmdlet in PowerShell?

PowerShell is so vast and can manage so many platforms and technologies. It’s easy to get deep in one module or topic. But the core features and language of PowerShell is so often where the real power is. I want to encourage you readers to (re)familiarize yourself with how to use add-member cmdlet in PowerShell.

Consider this:

  1. PowerShell is the most flexible and amazing programming language for performing system administration and cross-platform scripting
  2. PowerShell is built using the .NET language where everything is represented as objects
  3. Add-Member is a core cmdlet of the PowerShell language – it’s been there from the beginning, and it’s THE way to ADD a property to an object
  4. It gets far less attention than other cmdlets that perform getting, sorting, filtering and formatting.

You have to agree… the poor, lowly “Add-Member” command has to be the most underrated and underappreciated cmdlet!

But after working with PowerShell and mastering some basic skills, you’re going to want to start doing more with your objects than just taking bulk operations on them, or sending them through the pipeline to be filtered, sorted and formatted.

You will want to keep your object AND modify it by adding a property to it.  That’s exactly what Add-Member can do for you!

Parameters, Descriptions and Definitions

I’m going to give you some great use cases and examples of how to use the Add-Member cmdlet in PowerShell to extend an existing object with a new property.  But just in case we need a little help in understanding the basics of properties, members, methods, and objects

Object

In PowerShell, the “things” we work with are all objects.

  • Files
  • Services
  • Websites
  • Office365 Users
  • Active Directory Domains
  • Virtual Machines

No matter whether you’re dealing with something large or small, each “thing” you use in PowerShell is an object.  PowerShell provides more details about objects in the Microsoft PowerShell documentation or from the help files, so I won’t go DEEP into objects…

Just enough to say that OBJECTS end up being the NOUNS that we work with in PowerShell, and those OBJECTS have members that give us ways to work with the objects

Member

So the object is the THING that we’re working with…  and OBJECTS have MEMBERS.

Members are either:

  1. Properties that describe them, or
  2. Methods that define the actions they can take.

Membertype

There are two main types of members: 

  1. methods, which are the actions that an object can perform, and
  2. properties, which describe some part of the object.

In addition to these 2 “main” types of members, there are several member types which all fall under the category of “property”. 

Here’s the list of PS MemberTypes as of version 6.2; It doesn’t change much between versions, but it could have a new PSMemberType show up as PowerShell continues to grow.

What’s important to note in the context of ADD-MEMBER is that these are the only member types we can add:

  • NoteProperty
  • AliasProperty
  • ScriptProperty
  • CodeProperty
  • ScriptMethod
  • CopyMethod

Property

Property members are great at describing the object. 

“How large is a file?” – the answer is stored in the LENGTH property of the file object.

“What is the username?”, “Is the VM powered on?”, “Is the user account disabled?” – the answers to all of these questions are answered in the PROPERTIES of the OBJECTS.

So…

“What is the difference between a property and a noteproperty and all these other kinds of properties?”

“Property”, by itself, is the most generic form. It’s defined by type definitions, which are usually well documented parts of the language or the module you’re working with.

For any properties that you add to an object, you will NOT be adding generic “property” members.  Instead, you’ll be adding a specific property type.

NoteProperty:  A simple property, can be used for just about anything. Integers, Booleans, Null values, Strings, Decimals, and even complex objects can be attached to objects with noteproperty members.

AliasProperty: Gives you a way to add a “nickname” or alternate name for a property. It becomes a pointer to the original property, so changing either one will change both.

$file = Get-ChildItem .\FileName   
$pic | Add-Member -NotePropertyName "Owner" -NotePropertyValue "Michael"
$pic.Owner
$pic | Add-Member -MemberType AliasProperty -Name "Boss" -Value "Owner"
$pic.Boss
$pic.Owner = "Michael Simmons"
$pic.Boss
ScriptProperty: A property that is calculated by processing a script.

ScriptProperty: A property that is called by processing a script.

ScriptMethod: Add a method to your object that processes a scriptblock when called.

CodeProperty: Code properties get their values by referencing a method of a .NET object.

CodeMethod: Add a method to your object that references a method of a .NET object.

Between these few property and method membertypes, you’re able to add members to your object to suit your specific needs.

Parameter and Options

Learning how to use the add-member cmdlet in PowerShell is going to take some practice. But keep trying to make it part of your arsenal! It’s too valuable to ignore!

Now that we have a full understanding of object members, we can explore the parameters an options of the Add-Member cmdlet.

InputObject

InputObject is the object that you’re adding a member to.

This parameter is mandatory!

InputObject also accepts pipeline input. So if you’ve got an object stored in $a…

$a = New-Object -TypeName "PSCustomObject"
#These next two lines do the same thing
$a | Add-Member -MemberType NoteProperty `
  -Name "FirstName" -Value "Michael"
Add-Member -InputObject $a -MemberType NoteProperty `
  -Name "FirstName" -Value "Michael"

Name

This parameter is mandatory in most parameter sets

Name is the name of the member, whether it’s a method or property. Methods on objects are called by using the parenthesis () to invoke the method, and any arguments that you pass into the method are contained inside the parenthesis. But for the purposes of Add-Member, you won’t include parenthesis in the name of your member, even for methods.

Value

Value is what the member is set to. In the case of a NoteProperty, it could be anything from a simple object, like a STRING or an INTEGER, or it could be a complex object like a hashtable or even another .NET object.

SecondValue

SecondValue is an interesting parameter. At first glance it looks like a way to provide 2 values to the same property. But it takes on a slightly different form depending on the member type that is added.

SecondValue is only available for AliasProperty, ScriptProperty, CodeProperty and CodeMethod.

How SecondValue is used (by Membertype)
AliasProperty Used to cast the property as a specific type (integer or string, for example)
ScriptProperty A scriptblock that sets the value  
CodeProperty A reference to a method on a .NET object that sets the value of the property
CodeMethod A reference to a method on a .NET object that calls a “Set” method


NotePropertyName

NoteProperty is so widely used, it started to become synonymous with Add-Member. In fact, as Jeffrey Snover wrote in “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.”

-Jeffrey Snover,
PowerShell Chief Architect

NotePropertyName is a response to this interaction with Add-Member. Since NoteProperty was the biggest use case for Add-Member, it only makes sense to make that easier.

When using the NotePropertyName, you don’t need to specify the MemberType by parameter. It’s already taken care of for you.

NotePropertyValue

When using the NotePropertyName parameter, you don’t have to use the -MemberType parameter, but you still need to specify a value for your property.  NotePropertyValue is the right parameter to use for that.

$a = New-Object PSCustomObject
Add-Member -InputObject $a -NotePropertyName "Size" -NotePropertyValue 14 

NotePropertyMembers

I love this parameter!

It’s similar to creating a PSCustomObject through a hashtable. But in this case, it’s a fast, simple way to add a bunch of properties to an object.

$a = New-Object PSCustomObject
Add-Member -InputObject $a -NotePropertyMembers @{
Size = 14
First = "Michael"
Last = "Simmons"
}

TypeName

TypeName sets the name of the object type. If you use the GetType() method of an object, you can see the name of the type that the object is. It could be system.string or int32 or system.processinfo.

Or, you could include the -TypeName parameter and your object is no longer a PSCustomType object but a type named “MySuperObject”.

What does FORCE do on Add-Member?

Force creates the member on the object even if it already exists, overwriting any previous values of the member.

Notice in the above examples for NotePropertyValue and NotePropertyMembers, both add a “Size” NoteProperty to the object stored in the $a variable. Without the -force, the second command would give an error that the Size member already exists on the object.  If you include the -Force option, there is no error message and the Size member is re-added to the object!

Parameter Sets

Parameter Sets are different ways to utilize a command, and with the examples we just gave about NotePropertyName and NotePropertyValue, it’s easy to understand.

If you use the NotePropertyName parameter, there’s no need to use the MemberType parameter to specify you’re adding a NoteProperty.  The NotePropertyName and NotePropertyValue are used in one ParameterSet. Once you specify the MemberType by using the MemberType parameter, you are now using the “MemberSet” parameter set and the NotePropertyName parameter is not available to you.

Parameter Set Notes about it
MemberSet Have to specify the member type that you’re adding
TypeNameSet Can be used without adding other members to the object, just setting the object type nameTypeName can also be included with any other parameter set
NotePropertySingleMemberSet Uses NotePropertyName and NotePropertyValueSince NoteProperty has no use for the SecondValue, that parameter is not available in this set
NotePropertyMultiMemberSet Uses NotePropertyMembersSince NoteProperty has no use for the SecondValue, that parameter is not available in this set

So… That’s all the details about Add-Member that you could ever want to know…

How about some examples in code?

How to Use Add-Member Cmdlet in PowerShell

Using Add-Member used to be so cumbersome! Especially for creating custom objects – it was this tedious process of creating the object with one line of code, then going though and calling Add-Member over and over, once per line until your object has all it’s properties. 

Creating Custom Object

We can still use several variations of this, and the advances in Add-Member make it less painful.  We can use the NoteProperty parameter sets to simplify the language, and make bulk changes in NoteProperty members with a hashtable.

$a = New-Object -TypeName PSCustomObject
$a | Add-Member -NotePropertyMembers @{
Name = "Michael"
Language = "PowerShell"
OS = "Windows"
}

Using Casting as an Alternative to Add-Member

Thankfully, we can also use casting as an alternative. This means that our object can start with a set of properties. Here’s how that looks:

$a = [PSCustomObject]@{
Name = "Michael"
Language = "PowerShell"
OS = "Windows"
}

This doesn’t stop us from using Add-Member, it just gives us a great starting point with a custom object. If you have more members to add later in your script, you’ve still got Add-Member to get you what you need!

How to use the add-member cmdlet in a PowerShell Loop

There are several ways to use Add-Member in a loop!

Let’s look at a few. The looping options in PowerShell are:

  • For loops
  • Do loops (Do/While and Do/Until)
  • While loops

For Loops are especially good at processing items sequentially or operating on each item in an array.

I am having a little trouble coming up with a use case for adding members in a for loop. Here’s how the code looks, but my mind keeps coming back to using the counter to get the index of an item in an array.

Alright… so without any great reason I can think of for using Add-Member in a For loop… Here it is anyway:

$a = New-Object -TypeName "PSCustomObject"
For ($i = 0; $i -lt 3; $i++) {
  Add-Member -InputObject $a `
    -NotePropertyName "Index$($i)" -NotePropertyValue $i
}

Output:

PS C:\WINDOWS\system32> $a
Index0 Index1 Index2
------ ------ ------
0      1      2

Then, there’ creating a collection of objects, and storing them in an array, then performing Add-Member operations on each item in the collection.

Foreach is a mechanism for that.

Using Foreach 

Foreach is a more intuitive use for Add-Member. 

Here’s an example of using Foreach as a way to perform operations on each item in a collection, and referencing those items to perform lookups on a 2nd collection.

$Services = Get-Service
$Services | Where Status -eq "Running"
$CimServices = Get-CimInstance -ClassName Win32_Service
$Services | Foreach {
$ProcessId = $CimServices | Where Name -eq $_.Name | `
  Select-Object -ExpandProperty ProcessId
Add-Member -InputObject $_ `
   -NotePropertyName "ProcessId" -NotePropertyValue $ProcessId
}
$Services | select Name, ProcessId

In a Pipeline

The Foreach is great when you want to perform individual operations, or different property values,  on each item in a collection.

But if you want to put the SAME property on each member of a collection, there’s a better way.

By using the pipeline you can use the objects in an array as the inputs to the Add-Member cmdlet.

$Today = Get-Date
$Services | Add-Member -MemberType NoteProperty `
  -Name "CollectionDate" -Value $Today
$Services | Select-Object Name, CollectionDate

If it Doesn’t Exist

There can be a time when you want to add a member, but ONLY if that member doesn’t exist.

For this, using the -FORCE parameter wouldn’t help since it would overwrite in cases where the member already exists.  Instead, you need to do one of these:

  • Use and IF/THEN Statement that checks for the existence of the property.
Foreach ($item in $list) {
if (-not ($item | Get-Member -Name "AProperty")) {
Add-Member `
  -InputObject $item `
  -NotePropertyName "AProperty" `
  -NotePropertyValue "A Value"
 }
}
  • Use and ErrorAction parameter to silently continue on even though the Add-Member spits out an error when it tries to add an already existing member.
Foreach ($item in $list) {
  $item | Add-Member `
    -NotePropertyName "AProperty" `
    -NotePropertyValue "A Value" `
    -ErrorAction "SilentlyContinue"
}

What if it Already Exists?

On the other hand, if you want to switch your thinking and have a use case to set the value of a property only if it already exists then you don’t want to use Add-Member.  

Just use an assignment operator (like ‘=’ or ‘+=’) to set the value of the property that already exists.

Saving your work

It’s a shame to go through all of this effort to learn how to use add-member cmdlet in PowerShell scripts and start customizing your list of objects, just to shut down your terminal and lose your work. Don’t do that!

Instead, save your newly updated list of objects! You have several great options for saving object sets in all versions of PowerShell, including the latest PowerShell Core versions that run on Mac, Linux or Windows.

Exporting and Importing as XML

Exporting as an XML file is pretty easy, and is not hard to read as a file either. It’s not my favorite but it gets the job done.

$list | Export-Clixml -Depth 2 -Encoding UTF8 -Path C:\Exports\MyList.xml  

Use the -DEPTH parameter to help break up complex objects that are stored as a property, otherwise you’ll end up with a property described in your XML file that just lists the name of the object type that is stored in the property.

JSON

I love the readability of a JSON file, so this is my preferred export.  The “DEPTH” parameter is used the same was as exporting an XML

$list | ConvertTo-Json -Depth 2 -Compress | Out-File C:\Exports\MyList.json

The JSON cmdlet doesn’t automatically write to a file.

You may want to have the JSON object returned to the pipeline so it can be used as the body of an API call. So just pipe the JSON object to a file using Out-File to create your object export in JSON format.  

Note the -COMPRESS parameter, which doesn’t ZIP your JSON object. It just removes all the white-space.

Compressing your JSON makes it harder to read but if it’s just going to be read by a computer, an API, or another script you write later, then you don’t need the white space anyway.

CSV

If you’ve got a flat object, with not a lot of complex properties, then you may also really like the CSV option to export your list of PowerShell objects.

$list | Export-CSV -Path C:\Exports\MyList.csv

The upside is that it’s easy to read, especially for multi-line objects, and can even be opened in Excel. Then just save it as an Excel Workbook and share your work with your boss.

Recapping How to Use the Add-Member Cmdlet in PowerShell

Since I started looking closer at Add-Member, I’ve increased my overall proficiency with it. Where before it would be kind of an afterthought, I now am quick to add what I want to my list of objects. How to use add-member cmdlet in PowerShellSince I started looking closer at Add-Member, I’ve increased my overall proficiency with it. Where before it would be kind of an afterthought, I now am quick to add what I want to my list of objects.

VMs suddenly have a more direct PowerState property on them while I’m working on them.

Properties of cloud resources that are nested 3 levels deep in a complex object are surfaced and presented as full-on properties of the root object.

If Add-Member was the most underrated cmdlet in PowerShell before, for me it will stop being underappreciated.

It’s a GREAT cmdlet and so useful in so many ways! My scripting is better because of it!