More Answers to Your PowerShell Problems: Using and Extending Objects

In a previous article, we began exploring possible answers to a PowerShell question. If you missed that article, take a moment to get caught up, otherwise this article might be a bit confusing.


The last option for the original problem that I want to explore is to use PowerShell’s Add-Member cmdlet. This is similar to using Select-Object,  except it updates the original object. So let’s start again with initial values.

$amount = 123.45
$interest = .12

If you pipe $amount to Get-Member, you’ll see that there are no properties. I’ll add one for the value itself.

$amount | Add-Member -MemberType ScriptProperty -Name Value -Value {$this}

I need to use a ScriptProperty, because I must have PowerShell evaluate the object. In this context, this is reflected by $this, as opposed to $_. I can create a static note property for the rate.

$amount | Add-Member -MemberType NoteProperty -Name Percent -Value ($interest *100)

And finally scriptproperties to calculate new values:

$amount | Add-Member -MemberType ScriptProperty -Name Increase -value {$this * ($this.Percent/100)}
$amount | Add-Member -MemberType ScriptProperty -Name NewValue -value {$this + $this.Increase}

These new properties are now reflected with Get-Member.

Viewing new properties (Image Credit: Jeff Hicks)
Viewing new properties (Image Credit: Jeff Hicks)

To view them, I need to specify them.

$amount | select *

Values from added properties (Image Credit: Jeff Hicks)
Values from added properties (Image Credit: Jeff Hicks)

As an added bonus, I can modify a property value and get updated results.
Updating the object (Image Credit: Jeff Hicks)
Updating the object (Image Credit: Jeff Hicks)

But if I change the variable $amount, all of my changes are discarded.
New object discards new properties (Image Credit: Jeff Hicks)
New object discards new properties (Image Credit: Jeff Hicks)


Now that I have some code that works and understand the limitations, I can create a re-usable function. Are you seeing a pattern here?

Function Update-Value {
[cmdletbinding()]
Param([double]$Amount,[double]$Percent)
#convert percent back into the decimal format
$i = $percent/100
Write-Verbose "Updating $amount with $i"
<#
PowerShell doesn't seem to like trying to modify an object in use
as a parameter so we'll make a copy of it
#>
$result = $amount
$result | Add-Member -MemberType ScriptProperty -Name Value -Value {$this} -force
$result | Add-Member -MemberType NoteProperty -Name Percent -Value ($i *100) -force
$result | Add-Member -MemberType ScriptProperty -Name Increase -value {$this * ($this.Percent/100)} -Force
$result | Add-Member -MemberType ScriptProperty -Name NewValue -value {$this + $this.Increase} -force
#write the new object to the pipeline
$result
}

The functions in this article are very simple with no help, parameter validation, or error handling, primarily because these are more demonstrations than production level tools. But this works.

Testing the Update-Value function (Image Credit: Jeff Hicks)
Testing the Update-Value function (Image Credit: Jeff Hicks)

All I’m doing here is updating properties on number type of object. It is getting a bit tedious to remember to use Select-Object. So let’s fix that with a new typename. I can test with the variable I have now and once that works, then go back and revise the function.
Right now, $a has these type names.
Viewing type names (Image Credit: Jeff Hicks)
Viewing type names (Image Credit: Jeff Hicks)

This is important because the first name in the list helps PowerShell determine how to format and display the object on your screen. I don’t want to modify the System.Double type ,so I’ll insert my own.

$a.psobject.TypeNames.Insert(0,"My.IncreaseValue")

The type name can be anything you want as long as it is unique. Now look at typenames for $a

Viewing the new type name (Image Credit: Jeff Hicks)
Viewing the new type name (Image Credit: Jeff Hicks)

Because I have not provided any instructions to PowerShell in the way of type or format extensions, PowerShell will display all properties by default.
Display the object with the new type name (Image Credit: Jeff Hicks)
Display the object with the new type name (Image Credit: Jeff Hicks)

You could use this same technique with the function that uses [PSCustomObject].
Here’s the revised function. Note that you need to update the type names before you write the object to the pipeline.

Function Update-Value {
[cmdletbinding()]
Param([double]$Amount,[double]$Percent)
#convert percent back into the decimal format
$i = $percent/100
Write-Verbose "Updating $amount with $i"
<#
PowerShell doesn't seem to like trying to modify an object in use
as a parameter so we'll make a copy of it
#>
$result = $amount
$result | Add-Member -MemberType ScriptProperty -Name Value -Value {$this} -force
$result | Add-Member -MemberType NoteProperty -Name Percent -Value ($i *100) -force
$result | Add-Member -MemberType ScriptProperty -Name Increase -value {$this * ($this.Percent/100)} -Force
$result | Add-Member -MemberType ScriptProperty -Name NewValue -value {$this + $this.Increase} -force
#update typename
$result.psobject.typenames.insert(0,"My.IncreaseValue")
#write the new object to the pipeline
$result
}

Testing the revised function (Image Credit: Jeff Hicks)
Testing the revised function (Image Credit: Jeff Hicks)


Now I have objects in the pipeline that I can use like I would anything else. To sum it all up, the answer to the question that started off this mini-series is write a script that uses objects in a PowerShell pipeline. It is not that difficult and hopefully you picked up a few new ideas along the way.