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.
ManageEngine ADManager Plus - Download Free Trial
Exclusive offer on ADManager Plus for US and UK regions. Claim now!
One way you might accomplish this is to create a new, custom object with the New-Object cmdlet.
#requires -version 2.0 Function Join-Object { <# .Synopsis Join multiple objects together into a single object. .Description This function is designed to join, or merge two or more objects together. You can either pipe objects via cmdlets to Join-Object or specify a comma separated list of objects. A new object will be created with all unique properties and values from the original objects. If a property is duplicated among objects but with a different value, then it will be renamed. For example, if two objects both have a Name property but with different values, the custom object will have properties of Name and Name_1. Join-Object works best with input objects that themselves are not arrays or collections of other objects, but it can be done. The intent was to be able to merge related information into a single object that you could then export or convert. If you are joining WMI objects, you might prefer to use the Select-WMI function to strip out system properties like __CLASS. You can download this function from https://jdhitsolutions.com/blog/2010/05/select-wmi/. By default the new object will contain a property called _TypeInformation which will store an array of type information from the original objects. Use -NoTypeInformation to skip this. You can also include the original source objects in their "raw" state as part of the new object. Specify -IncludeOriginal to store the input objects as an array in the _SourceObject property. .Parameter InputObject Any valid PowerShell object. .Parameter IncludeOriginal If specified, the original input object will be stored in an array and be defined in the _SourceObject property on the new, custom object. .Parameter NoTypeInformation By default, each input object's type is stored in an array and defined in the _TypeInformation property on the new, custom object. If you do not want this information, then specify this switch. .Example PS C:\> (gwmi win32_computersystem),(gwmi win32_bios) | join-object Combine two WMI objects into a single object. .Example PS C:\> (gwmi win32_computersystem),(gwmi win32_bios) | select-wmi | join-object -NoTypeInformation Combine two WMI objects into a single object, but without the class properties. Select-WMI is a third-party developed function you can download from https://jdhitsolutions.com/blog/2010/05/select-wmi/ .Example PS C:\> $computers | foreach { (gwmi win32_operatingsystem -ComputerName $_),(gwmi win32_computersystem -ComputerName$_) | select-wmi | join-object} | export-csv c:\temp\sysreport.csv -NoTypeInformation Where $computers is a collection of computernames, get WMI information from two classes (win32_operatingsystem and win32_computersystem). Join these two objects, stripping out system properties and export the results to a csv file. Each computer will have it's own custom object comprised of both WMI classes. .Example PS C:\> $data=$fso.GetFile("c:\pagefile.sys"),$fso.GetDrive("c:") | join-object -IncludeOriginal $fso is a COM FileSystemObject. The results of the two methods are joined and safved to $data. You can view the original objects by looking at $data._SourceObjects .Inputs Any valid object .Outputs A custom object .Link New-Object .Link https://jdhitsolutions.com/blog/2010/05/join-object/ .Notes NAME: Join-Object VERSION: 1.0 AUTHOR: Jeffery Hicks (@JeffHicks) LASTEDIT: May 12, 2010 Learn more: PowerShell in Depth: An Administrator's Guide PowerShell Deep Dives Learn PowerShell 3 in a Month of Lunches Learn PowerShell Toolmaking in a Month of Lunches "Those who forget to script are doomed to repeat their work." **************************************************************** * DO NOT USE IN A PRODUCTION ENVIRONMENT UNTIL YOU HAVE TESTED * * THOROUGHLY IN A LAB ENVIRONMENT. USE AT YOUR OWN RISK. IF * * YOU DO NOT UNDERSTAND WHAT THIS SCRIPT DOES OR HOW IT WORKS, * * DO NOT USE IT OUTSIDE OF A SECURE, TEST SETTING. * **************************************************************** #> [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 #this script also defines an alias for this function. Set-Alias -Name jo -Value Join-Object
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.