functions/Merge-DryADPSObjects.ps1
<#
This is an AD Config module for use with DryDeploy, or by itself. Copyright (C) 2021 Bjørn Henrik Formo (bjornhenrikformo@gmail.com) LICENSE: https://raw.githubusercontent.com/bjoernf73/dry.module.ad/main/LICENSE This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. #> function Merge-DryADPSObjects { [CmdLetBinding()] param ( $FirstObject, $SecondObject, [Switch]$PreferSecondObjectOnConflict, [Switch]$FailOnConflict ) try { # This will accumulate the result $Private:Resultobject = New-Object -TypeName psobject $Private:ProcessedConflictingPropertyNames = @() # is both are arrays, merge if (($FirstObject -is [Array]) -and ($SecondObject -is [Array])) { $Private:ResultArray += $FirstObject $Private:ResultArray += $SecondObject return $Private:ResultArray } elseif ( ($FirstObject -is [string]) -and $SecondObject -is [string] ) { # This happens when properties are identical in above iterations. By default, the property from # $FirstObject is returned, unless the switch $PreferSecondObjectOnConflict is passed - then # the property from $SecondObject is returned. In any case, if the switch $FailOnConflict, # is passed, we throw an error if ($FailOnConflict) { throw "There was conflict (identical properties) and you passed -FailonConflict" } else { if ($PreferSecondObjectOnConflict) { return $SecondObject } else { return $FirstObject } } } elseif ( ($FirstObject -is [PSCustomObject]) -and $SecondObject -is [PSCustomObject] ) { # Iterate through each object property of $FirstObject foreach ($Property in $FirstObject | Get-Member -Type NoteProperty, Property) { # does SecondObject have a matching node? if ($null -eq $SecondObject.$($Property.Name)) { # $SecondObject does not contain the current property from $FirstObject, so # the property can be added to $Private:Resultobject as it is $Private:Resultobject | Add-Member -MemberType $Property.MemberType -Name $Property.Name -Value $FirstObject.($Property.Name) } else { # $SecondObject contains the current property from $FirstObject, so # the two must be merged. Call Merge-DryADPSObject $Private:Resultobject | Add-Member $Property.MemberType -Name $Property.Name -Value ( Merge-DryADPSObjects -FirstObject ($FirstObject.$($Property.Name)) -SecondObject ($SecondObject.$($Property.Name)) -PreferSecondObjectOnConflict:$PreferSecondObjectOnConflict -FailOnConflict:$FailOnConflict) $Private:ProcessedConflictingPropertyNames += $Property.Name } } # Members in $SecondObject that are not yet processed, has no # match in $FirstObject, and may be added to the result as is foreach ($Property in $SecondObject | Get-Member -type NoteProperty, Property) { if ($Private:ProcessedConflictingPropertyNames -notcontains $Property.Name) { olad d "Trying to add property '$($Property.Name)', type '$($Property.MemberType)', Value '$($SecondObject.($Property.Name))' " $Private:Resultobject | Add-Member -MemberType $Property.MemberType -Name $Property.Name -Value $SecondObject.($Property.Name) } else { olad d "Property '$($Property.Name)' is already processed" } } return $Private:Resultobject } else { olad e "FirstObject type: $($($FirstObject.Gettype()).Name) (Basetype: $($($FirstObject.Gettype()).BaseType))" $PSCmdlet.ThrowTerminatingError($_) } } catch { $PSCmdLet.ThrowTerminatingError($_) } } |