Save to My DOJO
Table of contents
The best thing about working with PowerShell is that everything is an object. This makes it easy to work with the properties of the thing you need to manage without a lot of scripting or complex text parsing. For example, when using the Hyper-V PowerShell module it is trivial to get specific details of a virtual machine.
There are many properties to this object. You can pipe the object to Get-Member to see the definition or Select-Object to show all properties and their values.
Get-VM Win10 | Get-Member Get-VM Win10 | Select-object -Property *
But what you want more? I usually do. For example, there is a CreationTime property for the virtual machine object. I’d like to be able to report on how old the virtual machine is. For a one-and-done approach I could write a command like this:
Get-VM | Select-Object -Property Name,CreationTime, @{Name="Age";Expression={(Get-Date) - $_.creationtime}} | Sort-Object -Property Age -Descending
Or maybe I’d like the MemoryAssigned value to be formatted in MB.
Get-VM | Select-Object -Property Name,State, @{Name="MemoryAssignedMB";Expression = {$_.memoryassigned/1mb}}
The syntax is to define a hashtable with 2 keys: Name and Expression. The Name value is what you want to use for your custom property name and the Expression is a PowerShell scriptblock. In the scriptblock you can run as much code as you need. Use $_ to reference the current object in the pipeline. In the last example, PowerShell looks at the MemoryAssigned value for the CentOS virtual machine object and divides it by 1MB. Then it moves on the DOM1 and divides the value (2147483648) by 1MB to arrive at 2048 and so on.
Using Select-Object this way is great for your custom scripts and functions. But suppose I want to get these values all the time when working with the object in the console interactively. I don’t want to have to type all that Select-Object code every single time. Fortunately, there is another approach.
PowerShell’s Extensible Type System
PowerShell has an extensible type system. This means you can extend or modify the type definition of an object. PowerShell takes advantage of this feature all the time. Many of the properties you’ve seen in common objects have been added by Microsoft. You can use the Get-TypeData cmdlet to view what additions have been made to a given type.
$td = Get-TypeData microsoft.hyperv.powershell.virtualmachine $td.Members
You can see these additions with Get-Member.
By now, you are thinking, “How can I do this?”. The good news is that it isn’t too difficult. Back in the PowerShell stone age you would have had to create a custom XML file, which you can still do. But I think it is just as easy to use the Update-TypeData cmdlet. I’ll give you some examples, but please take the time to read the full help and examples. One thing to keep in mind is that any type extensions you make last only as long as your PowerShell session is running. The next time you start PowerShell you will have to re-define them. I dot source a script file in my profile script that adds my type customizations.
Updating Type Data
The first thing you will need to know is the object’s type name. You can see that when piping to Get-Member.
$t = "Microsoft.Hyperv.Powershell.VirtualMachine"
When you define a new type you need to determine the membertype. This will be something like a ScriptProperty or Alias. A ScriptProperty is a value that is calculated by running some piece of PowerShell code. If you’ve created a custom type with Select-Object to test, as I did earlier, you will re-use that expression scriptblock. And of course, you need to define a name for your new property.
Update-TypeData -TypeName $t -MemberType ScriptProperty -MemberName Age -Value {(Get-Date) - $this.CreationTime} -force
With this code, I’m defining a new script property called Age. The value will be the result of the scriptblock that subtracts the CreationTime property of the object from now. The one difference to note compared to my Select-Object version is that here use $this instead of $_.
Update-TypeData -TypeName $t -MemberType ScriptProperty -MemberName MemoryAssignedMB -Value {$this.memoryassigned/1mb} -force Update-TypeData -TypeName $t -MemberType AliasProperty -MemberName Created -Value CreationTime -force
I went ahead and also created an alias property of “Created” that points to “CreationTime”. If you try to create a type extension that already exists PowerShell will complain. I typically use -Force to overwrite any existing definitions. But now I can see these new type members.
And of course, I can use them in PowerShell.
There’s really no limit to what you can do. Here is a script property that tells me when the VM started.
Update-Typedata -TypeName $t -MemberType ScriptProperty -MemberName LastStart -Value { if ($this.uptime -gt 0) { (Get-Date).AddSeconds(- ($this.uptime.totalseconds)) } else { $Null } } -force
and here is some code that takes the memory properties and creates an ‘MB’ version of each one. I’m using some PowerShell scripting and splatting to simplify the process.
$orig = 'MemoryMaximum','MemoryMinimum','MemoryStartup','MemoryDemand' $params = @{ TypeName = $t MemberType = "ScriptProperty" MemberName = "" Value = "" Force = $True } foreach ($item in $orig) { $params.MemberName = "$($item)MB" #get-vm win10 | select name,$item $sb = [scriptblock]::Create("`$this.$item /1mb") $params.Value = $sb Update-TypeData @params }
Now I have all sorts of information at my fingertips.
As long as I run the Update-Typedata commands I will have this information available. In my PSHyperVTools module, which you can install from the PowerShell Gallery, it will add some of these type extensions automatically, plus a few more. You can read about it in the project’s GitHub repository.
Does this look like something you would use? What sort of type extensions did you come up with? What else do you wish you could see or do? Comments and feedback are always welcome.
Not a DOJO Member yet?
Join thousands of other IT pros and receive a weekly roundup email with the latest content & updates!