Custom Object with Nested Properties – best way?

This topic contains 2 replies, has 3 voices, and was last updated by Profile photo of Poshoholic Poshoholic 3 years, 10 months ago.

  • Author
  • #9263
    Profile photo of stephenmbell

    So I am writing an eventual function to connect to one or many of our servers and retrieve various information using WMI:

    – Computer Name, Manufacturer, Model, Service Tag (serial number), OS, Architecture, # Of Processors, RAM, Disk).

    I have this working so far, I am just wondering if there is a better way to do it. Currently, this is how I am grabbing the disk –

    Get-WmiObject -Class Win32_LogicalDisk -ComputerName $ComputerName -Credential $Credential -Filter "DriveType=3" | 
            Select DeviceID, @{n='Disk Size (GB)';e={$_.size / 1gb -as [int]}}, @{n='FreeSpace (GB)';e={$_.freespace / 1gb -as [int]}} -OutVariable diskInfo

    Once I grab all of the different things I want, I create an ordered hash table:

    $infoHash = [ordered]@{
            'Manufacturer'= $sys.manufacturer
            'Model'= $sys.model 
            'ServiceTag' = $serviceTag
            'OperatingSystem' = $os.caption
            'Architecture' = $os.OSArchitecture
            'Processors'= $sys.NumberOfProcessors
            'Memory'= $sys.TotalPhysicalMemory / 1gb -as [int]
            'Disk' = $diskinfo 
    $computerInfo = New-Object -TypeName PSObject -Property $infohash

    When I take my custom object and pipe it to format-table, everything looks good – except for the diskInfo. I would love it if it had like a table in a table so that I could see it when I run this.

    Is there a way to either set this up properly with the objects or output it to the screen??



  • #9267
    Profile photo of Don Jones
    Don Jones

    You're constructing the object properly, but PowerShell's built-in formatting system can't handle object hierarchies in a "table in a table." If you pipe your output to Format-Custom, you'll see that it SEES the hierarchy, but Format-Table just can't do anything with it (nor could Format-List). Your only native solution would be to create your own custom format in a .format.ps1xml file, designed to handle the output of your command specifically. That's a pretty advanced topic, as I don't think the custom formats are actually documented in any real detail anyplace.

  • #9280
    Profile photo of Poshoholic

    You can get pretty close to what you are after without mucking around with any .format.ps1xml file stuff. Here's a script I put together to do this, and the output format is almost perfect for what you are looking for:

    $PSDefaultParameterValues = @{
        'Get-WmiObject:ComputerName' = 'localhost'
        'Get-WmiObject:Credential' = [System.Management.Automation.PSCredential]::Empty
    $serviceTag = 'P0w3r$h3llR0ck$!'
    $os = Get-WmiObject -Class Win32_OperatingSystem
    $sys = Get-WmiObject -Class Win32_ComputerSystem
    $ld = Get-WmiObject -Class Win32_LogicalDisk -Filter "DriveType=3"
    $diskInfoCollection = @()
    foreach ($item in $ld) {
        $diskInfo = [pscustomobject]@{
                    DeviceId = $item.DeviceID
            'Disk Size (GB)' = ($item.size / 1gb) -as [int]
            'FreeSpace (GB)' = ($item.freespace / 1gb) -as [int]
        Add-Member -Force -InputObject $diskInfo -MemberType ScriptMethod -Name ToString -Value {
            [System.Environment]::NewLine + (($this | Format-List * | Out-String) -replace '^\s+|\s+$')
        $diskInfoCollection += Add-Member -InputObject $diskInfo -TypeName My.DiskInfo -PassThru
    $computerInfo = [pscustomobject]@{
            'Manufacturer'= $sys.manufacturer
            'Model'= $sys.model 
            'ServiceTag' = $serviceTag
            'OperatingSystem' = $os.caption
            'Architecture' = $os.OSArchitecture
            'Processors'= $sys.NumberOfProcessors
            'Memory'= $sys.TotalPhysicalMemory / 1gb -as [int]
            'Disk' = $diskInfoCollection
    Add-Member -InputObject $computerInfo -TypeName My.ComputerInfo
    $computerInfo | Format-List *

    The magic is in the ToString method definition for the custom My.DiskInfo objects. That controls how those objects are resolved to string when you view the contents of the containing collection in your list of parent objects.

    You could also create a "display" property that shows the diskinfo as a nested table (maybe call it DiskTable), and have it contain the nested table as a string for easy reading.

You must be logged in to reply to this topic.