Version 1.0.0 Release #1
@@ -1,327 +1,15 @@
|
|||||||
function Merge-ConfigurationData {
|
$ModuleRoot = $PSScriptRoot
|
||||||
[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){
|
if([String]::IsNullOrWhiteSpace($ModuleRoot)){
|
||||||
$Output = $Deployment
|
throw "This compatibility loader must be dot-sourced from a file path. Use Import-Module './Merge-DSCConfigurationData.psd1' for module usage."
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach($Property in $Template.GetEnumerator()){
|
$PrivatePath = Join-Path -Path $ModuleRoot -ChildPath "Private"
|
||||||
if($Property.Value -is [System.Collections.Hashtable]){
|
$PublicPath = Join-Path -Path $ModuleRoot -ChildPath "Public"
|
||||||
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++){
|
$Private = @(Get-ChildItem -Path $PrivatePath -Filter "*.ps1" -File -ErrorAction Stop | Sort-Object -Property FullName)
|
||||||
$SearchKeyNames = @(Get-ConfigurationDataArrayMergeKeyNames -ArrayName $Property.Name -Item $Property.Value[$i])
|
$Public = @(Get-ChildItem -Path $PublicPath -Filter "*.ps1" -File -ErrorAction Stop | Sort-Object -Property FullName)
|
||||||
[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($File in @($Private + $Public)){
|
||||||
|
. $File.FullName
|
||||||
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 ", ")
|
|
||||||
}
|
}
|
||||||
|
|||||||
24
Merge-DSCConfigurationData.psd1
Normal file
24
Merge-DSCConfigurationData.psd1
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
@{
|
||||||
|
RootModule = "Merge-DSCConfigurationData.psm1"
|
||||||
|
ModuleVersion = "0.1.0"
|
||||||
|
GUID = "c1c7e70d-9049-4eaa-a3c9-44a424c35ef5"
|
||||||
|
Author = "Torsten Brendgen"
|
||||||
|
Copyright = "(c) Torsten Brendgen. All rights reserved."
|
||||||
|
Description = "Merges DSC configuration data from templates and deployment data."
|
||||||
|
PowerShellVersion = "5.1"
|
||||||
|
FunctionsToExport = @(
|
||||||
|
"Merge-ConfigurationData"
|
||||||
|
)
|
||||||
|
CmdletsToExport = @()
|
||||||
|
VariablesToExport = @()
|
||||||
|
AliasesToExport = @()
|
||||||
|
PrivateData = @{
|
||||||
|
PSData = @{
|
||||||
|
Tags = @(
|
||||||
|
"DSC",
|
||||||
|
"ConfigurationData"
|
||||||
|
)
|
||||||
|
ReleaseNotes = "Initial module layout."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
13
Merge-DSCConfigurationData.psm1
Normal file
13
Merge-DSCConfigurationData.psm1
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
$PrivatePath = Join-Path -Path $PSScriptRoot -ChildPath "Private"
|
||||||
|
$PublicPath = Join-Path -Path $PSScriptRoot -ChildPath "Public"
|
||||||
|
|
||||||
|
$Private = @(Get-ChildItem -Path $PrivatePath -Filter "*.ps1" -File -ErrorAction Stop | Sort-Object -Property FullName)
|
||||||
|
$Public = @(Get-ChildItem -Path $PublicPath -Filter "*.ps1" -File -ErrorAction Stop | Sort-Object -Property FullName)
|
||||||
|
|
||||||
|
foreach($File in @($Private + $Public)){
|
||||||
|
. $File.FullName
|
||||||
|
}
|
||||||
|
|
||||||
|
Export-ModuleMember -Function @(
|
||||||
|
"Merge-ConfigurationData"
|
||||||
|
)
|
||||||
37
Private/Copy-ConfigurationDataValue.ps1
Normal file
37
Private/Copy-ConfigurationDataValue.ps1
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
16
Private/Format-ConfigurationDataMergeKey.ps1
Normal file
16
Private/Format-ConfigurationDataMergeKey.ps1
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
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 ", ")
|
||||||
|
}
|
||||||
30
Private/Get-ConfigurationDataArrayMergeKeyNames.ps1
Normal file
30
Private/Get-ConfigurationDataArrayMergeKeyNames.ps1
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
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 @()
|
||||||
|
}
|
||||||
24
Private/Get-ConfigurationDataArraySearchItem.ps1
Normal file
24
Private/Get-ConfigurationDataArraySearchItem.ps1
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
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]
|
||||||
|
}
|
||||||
20
Private/Get-ConfigurationDataItemValue.ps1
Normal file
20
Private/Get-ConfigurationDataItemValue.ps1
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
20
Private/Test-ConfigurationDataItemContainsKey.ps1
Normal file
20
Private/Test-ConfigurationDataItemContainsKey.ps1
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
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]
|
||||||
|
}
|
||||||
18
Private/Test-ConfigurationDataItemHasKeys.ps1
Normal file
18
Private/Test-ConfigurationDataItemHasKeys.ps1
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
32
Private/Test-ConfigurationDataItemKeyMatch.ps1
Normal file
32
Private/Test-ConfigurationDataItemKeyMatch.ps1
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
20
Private/Test-ConfigurationDataItemWildcard.ps1
Normal file
20
Private/Test-ConfigurationDataItemWildcard.ps1
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
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 "*"
|
||||||
|
}
|
||||||
98
Public/Merge-ConfigurationData.ps1
Normal file
98
Public/Merge-ConfigurationData.ps1
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
132
Tests/Merge-ConfigurationData.Tests.ps1
Normal file
132
Tests/Merge-ConfigurationData.Tests.ps1
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
$script:ModuleRoot = Split-Path -Parent $PSScriptRoot
|
||||||
|
$script:SampleRoot = Join-Path -Path $script:ModuleRoot -ChildPath ".sample"
|
||||||
|
|
||||||
|
Import-Module (Join-Path -Path $script:ModuleRoot -ChildPath "Merge-DSCConfigurationData.psd1") -Force
|
||||||
|
|
||||||
|
Describe "Merge-ConfigurationData" {
|
||||||
|
It "merges wildcard instance templates into all deployment instances" {
|
||||||
|
$Template = Import-PowerShellDataFile (Join-Path -Path $script:SampleRoot -ChildPath "Template.psd1")
|
||||||
|
$Deployment = Import-PowerShellDataFile (Join-Path -Path $script:SampleRoot -ChildPath "Deployment.psd1")
|
||||||
|
|
||||||
|
$Result = Merge-ConfigurationData -Template $Template -Deployment $Deployment
|
||||||
|
$Instances = @($Result.NonNodeData.Services.SQLServer.Instances)
|
||||||
|
|
||||||
|
$Instances.Count | Should Be 2
|
||||||
|
($Instances.Name -contains "*") | Should Be $false
|
||||||
|
|
||||||
|
foreach($Name in @("MSSQLSERVER", "MSSQLSERVERTEST")){
|
||||||
|
$Instance = $Instances | Where-Object { $_.Name -eq $Name }
|
||||||
|
|
||||||
|
$Instance | Should Not BeNullOrEmpty
|
||||||
|
$Instance.Features | Should Be "SQLENGINE,FULLTEXT"
|
||||||
|
$Instance.ConfigurationOptions.Count | Should Be 4
|
||||||
|
$Instance.AdditionalScripts.Count | Should Be 6
|
||||||
|
$Instance.Memory | Should Not BeNullOrEmpty
|
||||||
|
$Instance.Directories | Should Not BeNullOrEmpty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
It "does not merge an exact instance template into another instance name" {
|
||||||
|
$Template = @{
|
||||||
|
NonNodeData = @{
|
||||||
|
Services = @{
|
||||||
|
SQLServer = @{
|
||||||
|
Instances = @(
|
||||||
|
@{
|
||||||
|
Name = "MSSQLSERVER"
|
||||||
|
Marker = "exact-only"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$Deployment = @{
|
||||||
|
NonNodeData = @{
|
||||||
|
Services = @{
|
||||||
|
SQLServer = @{
|
||||||
|
Instances = @(
|
||||||
|
@{ Name = "MSSQLSERVER" },
|
||||||
|
@{ Name = "MSSQLSERVERTEST" }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$Result = Merge-ConfigurationData -Template $Template -Deployment $Deployment
|
||||||
|
$Instances = @($Result.NonNodeData.Services.SQLServer.Instances)
|
||||||
|
|
||||||
|
($Instances | Where-Object { $_.Name -eq "MSSQLSERVER" }).Marker | Should Be "exact-only"
|
||||||
|
($Instances | Where-Object { $_.Name -eq "MSSQLSERVERTEST" }).Marker | Should BeNullOrEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
It "uses the name-key fallback for unknown arrays" {
|
||||||
|
$Template = @{
|
||||||
|
Databases = @(
|
||||||
|
@{
|
||||||
|
DatabaseName = "AppDb"
|
||||||
|
RecoveryModel = "Full"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
$Deployment = @{
|
||||||
|
Databases = @(
|
||||||
|
@{
|
||||||
|
DatabaseName = "AppDb"
|
||||||
|
Owner = "dbo"
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
DatabaseName = "LogDb"
|
||||||
|
Owner = "dbo"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
$Result = Merge-ConfigurationData -Template $Template -Deployment $Deployment
|
||||||
|
$Databases = @($Result.Databases)
|
||||||
|
|
||||||
|
($Databases | Where-Object { $_.DatabaseName -eq "AppDb" }).RecoveryModel | Should Be "Full"
|
||||||
|
($Databases | Where-Object { $_.DatabaseName -eq "LogDb" }).RecoveryModel | Should BeNullOrEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
It "uses Key and ValueName together for registry array merges" {
|
||||||
|
$Template = @{
|
||||||
|
Basic = @{
|
||||||
|
Registry = @(
|
||||||
|
@{
|
||||||
|
Key = "K1"
|
||||||
|
ValueName = "Enabled"
|
||||||
|
Default = "Template-K1"
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
Key = "K2"
|
||||||
|
ValueName = "Enabled"
|
||||||
|
Default = "Template-K2"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$Deployment = @{
|
||||||
|
Basic = @{
|
||||||
|
Registry = @(
|
||||||
|
@{
|
||||||
|
Key = "K1"
|
||||||
|
ValueName = "Enabled"
|
||||||
|
ValueData = "Deployment-K1"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$Result = Merge-ConfigurationData -Template $Template -Deployment $Deployment
|
||||||
|
$Registry = @($Result.Basic.Registry)
|
||||||
|
|
||||||
|
($Registry | Where-Object { $_.Key -eq "K1" }).Default | Should Be "Template-K1"
|
||||||
|
($Registry | Where-Object { $_.Key -eq "K1" }).ValueData | Should Be "Deployment-K1"
|
||||||
|
($Registry | Where-Object { $_.Key -eq "K2" }).Default | Should Be "Template-K2"
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user