Getting Started with PSCustomObject in PowerShell

It has always been very easy to create hashtables and arrays in PowerShell, but there are times that a generic object comes in handy. Both hashtables and arrays are collections of objects, but a PSCustomObject is constructed of properties and values. PSCustomObject’s can be stored in arrays and hashtables, but ultimately they are intended to be a collection of properties.

When you need to store data in a structured format that can be extended upon, or in a more ordered fashion, PSCustomObject works great!

Creating a PSCustomObject in PowerShell

The fastest and easiest way to create a PSCustomObject is to use the following method, which works in all versions of PowerShell 3.0 and above.

$Object = [PSCustomObject]@{}

You can also use the New-Object -TypeName PSObject -Property @{} which will work in earlier versions of PowerShell, but it is also slower. This speed comes into play when you need to create or manipulate many objects in a script.

You will notice that there appears to be a hashtable appended to the type declaration, [PSCustomObject], this is because we are passing the properties and values as key-values, best suited for a hashtable. The other benefit to this approach is that the PSCustomObject properties are ordered, unlike in a traditional hashtable where the keys will not maintain their initial order. Each of the created properties will be a NoteProperty like a typical PowerShell object.

Of course, we have created an empty PSCustomObject, which doesn’t do much good. How do we add properties? In the old days, we would use Add-Member to add properties to an existing object. There is an easier way, on initial object creation.

$Object = [PSCustomObject]@{
	'Property1' = 'Value1'
	'Property2' = 'Value2'
	'Property3' = 'Value3'
}
Untitled 2020 12 24T185746.402

Great! We have properties now, but often in a script, you may need to add an additional property. This is where Add-Member is still handy. You can use this with PSCustomObject as seen below.

Add-Member -InputObject $Object -MemberType 'NoteProperty' -Name 'Property Space' -Value 'Value4'
Untitled 2020 12 24T185746.402 1

Now that we have our custom object, read on to learn how to access the properties!

Returning Properties from a PSCustomObject

Just creating an object doesn’t do much good, so how do we go about retrieving the members? Just like any other typical PowerShell object, we can simply pass the name of the property using dot-notation to the object. There are three flexible ways to be aware of, as shown below.

# The standard way is to simply pass in the property name.
$Object.Property1
# Wrap the property name in quotes, single or double.
# This also allows you to return property names that have a space.
$Object.'Property1'
$Object.'Property Space'
# What if the property name is contained in a variable, pass that in too!
$Property = 'Property1'
$Object.$Property
# Bonus! If you use double-quotes, you can use variables within the name.
For($i = 1; $i -LT 5; $i++) {
  $Object."Property$i"
}

As you can see from above, there are a lot of ways that we can address a property using PSCustomObject. What else can we do with our PSCustomObject? To find out, read on!

Extending our PSCustomObject

Ok, so now that we have a basic object what can we do with it? Since this is a standard PowerShell object, we have the ability to use more than just a NoteProperty on the object. Most of the time you will just use the key-value pair that is a NoteProperty, but we can actually extend the object to use an evaluated ScriptBlock every time the object is accessed. Let’s create a new object with two properties, one that is a NoteProperty and the second, which is a ScriptBlock that just returns the output from Get-Date.

# Add a Scriptblock using the type accelerator of [ScriptBlock]{}.
# Wrap that declaration and code in parenthesis to then run the method of InvokeReturnAsIs().
# By using this method, you make sure to return just the output, not further encapsulated.
$Object = [PSCustomObject]@{
	'Property1' = 'Value1'
	'DateTime'  = ([ScriptBlock]{
    Get-Date
  }).InvokeReturnAsIs()
}
Untitled 2020 12 24T185840.576

You may notice on subsequent runs that the value of DateTime is not updating. That is because the output is evaluated on the creation of the object. If we want to have this code dynamically evaluated we need an actual ScriptProperty member type. To do this, we need to use Add-Member as shown in the following.

Add-Member -InputObject $Object -MemberType ScriptProperty -Name "DateTimeDynamic" -Value { Get-Date }
Untitled 2020 12 24T185901.619

As you can see from the above code, each time the code is run the DateTimeDynamic is evaluated and returned.

Bonus! Returning a Function as a Property

Even cooler is the ability to return a function as a property! Simply assign the function to a property name and it will run and store the value in the memory of the object the first time it is run.

Function Get-FormattedDateTime {
  Get-Date -Format "yyyyMMdd_hhmmss"
}

$Object = [PSCustomObject]@{
  'DateTimeFormatted' = Get-FormattedDateTime
}
Untitled 2020 12 24T185923.873

Conclusion

As you can see, PSCustomObject is incredibly useful. There are many possibilities that the object unlocks, especially with the ScriptBlock and ScriptProperty methods. With the ability to evaluate a function as a value as well, you can easily extend the object in to unique use cases. Explore PSCustomObject and see how it can be added to your scripts today!