Join Object

Related to some of the WMI stuff I’ve been working on lately is the idea of melding or joining objects. This comes about because I often see forum posts from administrators looking to collect information from different WMI classes but present it as a single object.

One way you might accomplish this is to create a new, custom object with the New-Object cmdlet.

#requires -version 2.0            

get-content computers.txt | foreach {
  $os=Get-WmiObject -class Win32_OperatingSystem -ComputerName $_
  $sys=Get-WmiObject -Class win32_Computersystem -ComputerName $_
  $bios=Get-WmiObject -Class win32_Bios -ComputerName $_            

  New-Object PSObject -Property @{
    Computername=$_.toUpper()
    OperatingSystem=$os.Caption
    LastBoot=$os.ConvertToDateTime($os.LastBootupTime)
    InstallDate=$os.ConvertToDateTime($os.InstallDate)
    BiosVersion=$bios.version
    TotalPhysMemMB=($sys.TotalPhysicalMemory)/1MB -as [int]
    Vendor=$sys.Manufacturer
    Model=$sys.model
  }
} #foreach

This will return an object like this for each computername:

InstallDate     : 8/16/2009 2:06:41 PM

Computername    : GODOT7

Model           : Latitude D800

LastBoot        : 5/5/2010 9:55:12 AM

OperatingSystem : Microsoft Windows 7 Ultimate

BiosVersion     : DELL   – 27d5061e

TotalPhysMemMB  : 1535

Vendor          : Dell Computer Corporation

If you know you always want this information, this is a great approach. But what if your needs a bit more whimsical? I’m not saying you would need to do it often, but would it be helpful to take 2 or more objects and merge them together? That’s what I did with my Join-Object function. This is only the function’s business part.

Function Join-Object {            

[CmdletBinding()]
Param(
    [Parameter(Position=0,
               ValueFromPipeline=$True,
               Mandatory=$True,
               HelpMessage="Enter a valid PowerShell object")]
    [object[]]$Inputobject,
    [switch]$IncludeOriginal,
    [switch]$NoTypeInformation
)
Begin {
    write-verbose "Starting $($myinvocation.mycommand)"
    #initialize a hash table for new properties
    $newProp=@{}
    #initialize a hash table to handle duplicate property names
    $duplicate=@{}
    #initialize an array to hold raw objects
    $raw=@()
    #initialize an array to hold object metadata
    $metatypes=@()
}            

Process {
  foreach ($parentobject in $InputObject) {
  #handle inputobjects that are arrays themselves
    foreach ($object in $parentobject) {
    $raw+=$object
    write-verbose "Adding type $($object.GetType().Fullname)"
    $metaTypes+=$object.GetType().Fullname
      $object | get-member -MemberType Properties | foreach {            

        if ($newProp.Contains($_.name) ) {
            write-Verbose "Property $($_.name) already defined                           $($newProp.item($_.name))"
            #skip properties that have duplicate values
            #only include the property if it has a different value
            #and then rename the property like myProperty_1
            if ($newProp.item($_.name) -ne $object.($_.Name)) {
                $duplicate.item($_.name)+=1
                $newName=$_.name+"_"+$duplicate.item($_.name)
                write-Verbose "Adding $newName =  $($object.($_.Name))."
                $newProp.Add($newName,$object.($_.name))
            }
        }
        else {
            Write-Verbose "Adding property $($_.name)"
            $newProp.Add($_.name,$object.($_.name))
        }
      } #foreach
     } #foreach object
  } #foreach parentobject            

} #Process            

End {
    if ($IncludeOriginal) {
        #add raw objects as a property
        Write-Verbose "Adding original raw objects"
        $newProp.Add("_SourceObjects",$raw)
    }            

    if ($NoTypeInformation) {
        write-verbose "Skipping meta type information"
    }
    else {
        #add unique object metadata as a property
        $newProp.Add("_TypeInformation",($metaTypes |         Select-object -unique))
    }            

    #write the new object to the pipeline
    New-Object PSObject -Property $NewProp
    write-verbose "Ending $($myinvocation.mycommand)"
}            

} #end Function

The full function includes comment-based help and can behave just like a cmdlet. You can either pipe objects into the function:

PS C:\> $a,$b | join-object

Or specify them as input objects

PS C:\> join-object (gwmi win32_bios),(gwm win32_operatingsystem)

Join-Object will get the properties for each object and copy them to a new object. If a property name is duplicated with duplicate values, it is only added once. But if two objects have the same property but different values, then each subsequent property is renamed in incremented, eg Name, Name_1, Name_2.

If using WMI objects, I suggest also using my Select-WMI function to strip out the system classes.

Join-Object works best with objects that are not collections of objects themselves. It will work, but gets a little tricky to sort out.

I also include metadata information in the new object based on the input objects. Each input object type is stored as an array in the _TypeInformation property. You can skip this by using the –NoTypeInformation parameter. Similarly, you can elect to include the “raw”, input objects with –IncludeOriginal. The new object will have a _SourceObjects property.

As always, I hope you’ll let me know what you think.

Post to Twitter Post to Plurk Post to Yahoo Buzz Post to Delicious Post to Digg Post to Facebook Post to FriendFeed Post to Google Buzz Post to Ping.fm Post to Reddit Post to Slashdot Post to StumbleUpon Post to Technorati

This entry was posted in PowerShell v2.0, Scripting and tagged , , , . Bookmark the permalink.

Comments are closed.