function Merge-ConfigurationData { [CmdletBinding()] Param( [Parameter(Mandatory=$true)] [System.Collections.Hashtable] $Template, [Parameter(Mandatory=$true)] [System.Collections.Hashtable] $Deployment, [Parameter(Mandatory=$false)] [System.Collections.Hashtable] $Output ) if($Output -eq $null){ $Output = $Deployment } foreach($Property in $Template.GetEnumerator()){ if($Property.Value -is [System.Collections.Hashtable]){ Write-Verbose "Key [$($Property.Name)] is a Hashtable" if($null -ne $Deployment.$($Property.Name)){ Write-Verbose "Key [$($Property.Name)] is present in Deployment Data" $Output.($Property.Name) = Merge-ConfigurationData -Template $Template.$($Property.Name) -Deployment $Deployment.$($Property.Name) -Output $Output.$($Property.Name) }else{ Write-Verbose "Key [$($Property.Name)] is not present in Deployment Data" $Output.Add($($Property.Name),(Copy-ConfigurationDataValue -Value $Template.$($Property.Name))) } }elseif($Property.Value -is [System.Collections.Specialized.OrderedDictionary]){ Write-Verbose "Key [$($Property.Name)] is a Ordered Dictionary" if($null -ne $Deployment.$($Property.Name)){ Write-Verbose "Key [$($Property.Name)] is present in Deployment Data" $Output.($Property.Name) = Merge-ConfigurationData -Template $Template.$($Property.Name) -Deployment $Deployment.$($Property.Name) -Output $Output.$($Property.Name) }else{ Write-Verbose "Key [$($Property.Name)] is not present in Deployment Data" $Output.Add($($Property.Name),(Copy-ConfigurationDataValue -Value $Template.$($Property.Name))) } }elseif($Property.Value -is [System.Array]){ Write-Verbose "$($Property.Name) is ein Array" Write-Verbose "Total Items in Template Array [$($Property.Value.Count)]" Write-Verbose "Total Items in Deployment Array [$($Deployment.$($Property.Name).Count)]" if($null -ne $Deployment.$($Property.Name)){ Write-Verbose "Array is defined in Deployment" $TemplateItems = for($i=0;$i -lt $Property.Value.Count; $i++){ $SearchKeyNames = @(Get-ConfigurationDataArrayMergeKeyNames -ArrayName $Property.Name -Item $Property.Value[$i]) [PSCustomObject]@{ Value = $Property.Value[$i] SearchKeyNames = $SearchKeyNames IsWildcard = Test-ConfigurationDataItemWildcard -Item $Property.Value[$i] -KeyNames $SearchKeyNames } } $TemplateItems = @($TemplateItems | Where-Object { -not $_.IsWildcard }) + @($TemplateItems | Where-Object { $_.IsWildcard }) foreach($TemplateItem in $TemplateItems){ $SearchKeyNames = @($TemplateItem.SearchKeyNames) if($SearchKeyNames.Count -eq 0){ Write-Verbose "No matching name key found for item in [$($Property.Name)]" $Output.$($Property.Name) += ,(Copy-ConfigurationDataValue -Value $TemplateItem.Value) continue } if($TemplateItem.IsWildcard){ $MatchingDeploymentItems = @($Deployment.$($Property.Name) | Where-Object { Test-ConfigurationDataItemHasKeys -Item $_ -KeyNames $SearchKeyNames }) }else{ $MatchingDeploymentItems = @($Deployment.$($Property.Name) | Where-Object { Test-ConfigurationDataItemKeyMatch -Left $_ -Right $TemplateItem.Value -KeyNames $SearchKeyNames }) } if($MatchingDeploymentItems.Count -gt 0){ foreach($DeploymentItem in $MatchingDeploymentItems){ $OutputItem = @($Output.$($Property.Name) | Where-Object { Test-ConfigurationDataItemKeyMatch -Left $_ -Right $DeploymentItem -KeyNames $SearchKeyNames })[0] Merge-ConfigurationData -Template $TemplateItem.Value -Deployment $DeploymentItem -Output $OutputItem | Out-Null } }else{ if($TemplateItem.IsWildcard){ Write-Verbose "Wildcard item [$(Format-ConfigurationDataMergeKey -Item $TemplateItem.Value -KeyNames $SearchKeyNames)] in [$($Property.Name)] matched no deployment items" }else{ Write-Verbose "Pair [$(Format-ConfigurationDataMergeKey -Item $TemplateItem.Value -KeyNames $SearchKeyNames)] not present" $Output.$($Property.Name) += ,(Copy-ConfigurationDataValue -Value $TemplateItem.Value) } } } }else{ Write-Verbose "Array is not defined in Deployment" $Output.$($Property.Name) = Copy-ConfigurationDataValue -Value $Template.$($Property.Name) } }else{ Write-Verbose "$($Property.Name) is a String or Integer Value" if($null -eq $Deployment.$($Property.Name)){ $Output.Add($Property.Name,(Copy-ConfigurationDataValue -Value $Template.($Property.Name))) }elseif($Deployment.$($Property.Name) -ne $Property.Value){ } } } return $Output } function Copy-ConfigurationDataValue { [CmdletBinding()] Param( [AllowNull()] $Value ) if($null -eq $Value){ return $null } if($Value -is [System.Collections.Specialized.OrderedDictionary]){ $Copy = [ordered]@{} foreach($Entry in $Value.GetEnumerator()){ $Copy[$Entry.Name] = Copy-ConfigurationDataValue -Value $Entry.Value } return $Copy } if($Value -is [System.Collections.Hashtable]){ $Copy = @{} foreach($Entry in $Value.GetEnumerator()){ $Copy[$Entry.Name] = Copy-ConfigurationDataValue -Value $Entry.Value } return $Copy } if($Value -is [System.Array]){ $Copy = @() foreach($Item in $Value){ $Copy += ,(Copy-ConfigurationDataValue -Value $Item) } return ,$Copy } return $Value } function Get-ConfigurationDataArraySearchItem { [CmdletBinding()] Param( [AllowNull()] $Item ) if($null -eq $Item){ return $null } if(-not ( ($Item -is [System.Collections.Hashtable]) -or ($Item -is [System.Collections.Specialized.OrderedDictionary]) )){ return $null } return @( $Item.GetEnumerator() | Where-Object { ($_.Value -is [String]) -and ($_.Name -like "*Name") } | Select-Object -First 1 )[0] } function Get-ConfigurationDataArrayMergeKeyNames { [CmdletBinding()] Param( [Parameter(Mandatory=$true)] [String] $ArrayName, [AllowNull()] $Item ) $MergeKeyMap = @{ Instances = @('Name') ConfigurationOptions = @('OptionName') AdditionalScripts = @('ScriptName') Templates = @('TemplateName') AllNodes = @('NodeName') Registry = @('Key','ValueName') } if($MergeKeyMap.ContainsKey($ArrayName)){ return $MergeKeyMap[$ArrayName] } $SearchItem = Get-ConfigurationDataArraySearchItem -Item $Item if($null -ne $SearchItem){ return @($SearchItem.Name) } return @() } function Test-ConfigurationDataItemContainsKey { [CmdletBinding()] Param( [AllowNull()] $Item, [Parameter(Mandatory=$true)] [String] $KeyName ) if($null -eq $Item){ return $false } if($Item -is [System.Collections.IDictionary]){ return $Item.Contains($KeyName) } return $null -ne $Item.PSObject.Properties[$KeyName] } function Get-ConfigurationDataItemValue { [CmdletBinding()] Param( [AllowNull()] $Item, [Parameter(Mandatory=$true)] [String] $KeyName ) if(-not (Test-ConfigurationDataItemContainsKey -Item $Item -KeyName $KeyName)){ return $null } if($Item -is [System.Collections.IDictionary]){ return $Item[$KeyName] } return $Item.$KeyName } function Test-ConfigurationDataItemHasKeys { [CmdletBinding()] Param( [AllowNull()] $Item, [Parameter(Mandatory=$true)] [String[]] $KeyNames ) foreach($KeyName in $KeyNames){ if(-not (Test-ConfigurationDataItemContainsKey -Item $Item -KeyName $KeyName)){ return $false } } return $true } function Test-ConfigurationDataItemKeyMatch { [CmdletBinding()] Param( [AllowNull()] $Left, [AllowNull()] $Right, [Parameter(Mandatory=$true)] [String[]] $KeyNames ) if($KeyNames.Count -eq 0){ return $false } foreach($KeyName in $KeyNames){ if(-not (Test-ConfigurationDataItemContainsKey -Item $Left -KeyName $KeyName)){ return $false } if(-not (Test-ConfigurationDataItemContainsKey -Item $Right -KeyName $KeyName)){ return $false } if((Get-ConfigurationDataItemValue -Item $Left -KeyName $KeyName) -ne (Get-ConfigurationDataItemValue -Item $Right -KeyName $KeyName)){ return $false } } return $true } function Test-ConfigurationDataItemWildcard { [CmdletBinding()] Param( [AllowNull()] $Item, [Parameter(Mandatory=$true)] [String[]] $KeyNames ) if($KeyNames.Count -ne 1){ return $false } if(-not (Test-ConfigurationDataItemContainsKey -Item $Item -KeyName $KeyNames[0])){ return $false } return (Get-ConfigurationDataItemValue -Item $Item -KeyName $KeyNames[0]) -eq "*" } function Format-ConfigurationDataMergeKey { [CmdletBinding()] Param( [AllowNull()] $Item, [Parameter(Mandatory=$true)] [String[]] $KeyNames ) $Pairs = foreach($KeyName in $KeyNames){ "$KeyName=$(Get-ConfigurationDataItemValue -Item $Item -KeyName $KeyName)" } return ($Pairs -join ", ") }