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.
