Microsoft 365


Active Directory


Windows Server


Microsoft Teams Day is back!



Extending Objects with PowerShell, Part 2

Jeff Hicks


Let’s continue our exploration of expanding the objects we get from running a PowerShell cmdlet. In the previous article, I demonstrated how to use Add-Member to define a new property that’s calculated every time you get it. In this article, we’ll continue looking at how we can use objects with PowerShell, this time by learning how to create alias with objects, as well as utilizing methods with objects.
Extending Objects in PowerShell Article Series:

$dc1 = Get-CimInstance win32_operatingsystem -ComputerName chi-dc01
$dc1 | Add-Member -MemberType ScriptProperty -Name Uptime -Value {(Get-Date) - $this.lastbootuptime}

You might also want to create alias property, which happens frequently in PowerShell. In the case of $DC1, it might be easier to use a property called OperatingSystem, which is much more meaningful than the original Caption property.

$dc1 | Add-Member -MemberType AliasProperty -Name OperatingSystem -value Caption -force

The OperatingSystem property in Windows PowerShell. (Image Credit: Jeff Hicks)

The OperatingSystem property in Windows PowerShell. (Image Credit: Jeff Hicks)

For the value parameter, simply specify the name of the original property.
The other type of custom property that you can define is referred to as a Noteproperty. This is a static value that’s defined when you define the property. You can use a simple string:

$dc1 | Add-Member -MemberType NoteProperty -Name Location -value "Chicago"

You can also use the result of a PowerShell expression.

$psversion = Invoke-Command {  $PSVersionTable.PSVersion } -computername $dc1.PSComputerName
$dc1 | Add-Member -MemberType NoteProperty -Name PSVersion -value $psversion

The value of $PSVersion is only calculated once since it’s unlikely to change. Now after a few lines of PowerShell, I’ve extended the original WMI object with information that’s meaningful to me and perhaps necessary for other internal IT processes.

There’s really no limit to what you can add or how many additional properties. The only limit is your imagination.
But as they say on late night television, “But wait, there’s more!”
You can also add methods to an object like $DC1. Generally the methods you add are doing something using one or more properties of the original object. How about adding a Ping method so that I can test if the computer is still reachable? We will accomplish this by defining a ScriptMethod.

$dc1 | Add-Member -MemberType ScriptMethod -Name Ping -value {Test-Connection -ComputerName $this.pscomputername -quiet} -force

As we did with ScriptProperty, the value is a PowerShell scriptblock. Use $this to reference the current object. In my example I am going to run the Test-Connection cmdlet, specifying the computername from the $DC1 object. I’m also telling Test-Connection to only return true or false. I can invoke this method like any other.

I can even define methods that take parameters. Remember, your scriptblock can be as complex as you need it to be and scriptblocks can take parameters.
To make this easier, I’ll break the process down into a few steps. First, I’ll define the parameterized scriptblock.

$getdisk = {
Get-CimInstance -classname Win32_logicalDisk -filter "DeviceID='$DeviceID'" -ComputerName $this.pscomputername

This scriptblock is designed to get disk information from the computer, defaulting to the C: drive. Now I will add another ScriptMethod.

$dc1 | Add-Member -MemberType ScriptMethod -Name GetDisk -value $getdisk -force

Using the –Force parameter will tell PowerShell to go ahead and overwrite any existing properties with the same name. You’ll find this very handy when developing your scriptblocks, because if you are like me, you won’t get everything right the first time. What does this look like?

By omitting the (), PowerShell didn’t try to execute the method. This is the best way I have found to determine any method parameters. Once I know, I can invoke the method.

Invoking the getdisk() method. (Image Credit: Jeff Hicks)

Invoking the getdisk() method. (Image Credit: Jeff Hicks)

You’ll notice that the scriptblock for GetDisk() gives me a complete object. Yes, I could have written the scriptblock to give me a single value like Size, but that limits me. I always like to have options.
Let’s put this all together.

if ($ {
$dc1 | Select PSComputername,Location,OperatingSystem,ServicePackMajorVersion,Uptime,
$c = $_.GetDisk()
else {
Write-Warning "$($dc1.PSComputerName) appears to be offline"

Finally, let’s put this all together for several computers using some existing CIM sessions.

$data = Get-CimSession | Get-CimInstance -ClassName Win32_OperatingSystem | foreach {
#add custom members
$_ | Add-Member -MemberType ScriptProperty -Name Uptime -Value {(Get-Date) - $this.lastbootuptime}
$_ | Add-Member -MemberType AliasProperty -Name OperatingSystem -value Caption
$_ | Add-Member -MemberType AliasProperty -Name ServicePack -value ServicePackMajorVersion
$_ | Add-Member -MemberType NoteProperty -Name Location -value "Chicago"
$psversion = Invoke-Command {$PSVersionTable.PSVersion } -computername $_.PSComputerName
$_ | Add-Member -MemberType NoteProperty -Name PSVersion -value $psversion
#use -passthru on the last step to write something to the pipeline
$_ | Add-Member -MemberType ScriptMethod -Name GetDisk -value {
Get-CimInstance -classname Win32_logicalDisk -filter "DeviceID='$DeviceID'" -ComputerName $this.pscomputername
} -PassThru

With these members in place, I can use them with the $data object however I want.

Or process everything.

$data | Select PSComputername,Location,OperatingSystem,ServicePack,InstallDate,Uptime,
   $c = $_.GetDisk()
PSVersion | Out-Gridview -Title "Server Data"

This is certainly a different way of managing with PowerShell and is something I want to continue to explore in the next article. I hope you’ll let me know what you think about all of this.

Most popular on petri

Article saved!

Access saved content from your profile page. View Saved