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.




