Join-Config.ps1
function Join-Config { <# .Synopsis Joins several DSC configurations .Description Joins several DSC configurations into a single DSC configuration .Link Split-Config .Example { configuration InstallWebServer_IIS { node localhost { WindowsFeature IIS { Ensure = “Present” Name = “Web-Server” } } } }, { configuration InstallWebServer_UrlRewrite { node localhost { Package UrlRewrite { #Install URL Rewrite module for IIS Ensure = "Present" Name = "IIS URL Rewrite Module 2" Path = "http://download.microsoft.com/download/6/7/D/67D80164-7DD0-48AF-86E3-DE7A182D6815/rewrite_2.0_rtw_x64.msi" Arguments = "/quiet" ProductId = "EB675D0A-2C95-405B-BEE8-B42A65D23E11" } } } } | Join-Config -Name InstallIISAndUrlRewrite #> [OutputType([ScriptBlock])] param( # The configuration [Parameter(Mandatory=$true,ValueFromPipeline=$true, Position=0)] [ScriptBlock[]] $Configuration, # The name of the new configuration. If this is not provided, the name of the first configuration will be used [string] $Name ) begin { $allModulesImported = New-Object Collections.ArrayList $ExplicitResourceNames = New-Object Collections.ArrayList $allVariablesUsed = New-Object Collections.ArrayList $allConfigs = New-Object Collections.ArrayList } process { $allConfigs.AddRange($Configuration) } end { #region Parse All Configurations $paramBlocks = @{} $allConfigParts = foreach ($config in $allConfigs) { $text = "$config" $tokens = [Management.Automation.PSParser]::Tokenize("$config", [ref]$null) for ($i =0; $i -lt $tokens.count; $i++) { if ($tokens[$i].type -eq 'keyword' -and $tokens[$i].Content -eq 'param') { $paramEnd = $i + 1 while ($tokens[$paramEnd].Type -ne 'GroupStart' -and $tokens[$paramEnd].Content -ne '(') { $paramEnd++ } $paramStart = $paramEnd $lastParamStart = $paramEnd $braceCount = 0 do { if ($tokens[$paramEnd].type -eq 'GroupStart' -and $tokens[$paramEnd].Content -eq '(') { $braceCount++ } if ($tokens[$paramEnd].type -eq 'GroupEnd' -and $tokens[$paramEnd].Content -eq ')') { $braceCount-- } if ($tokens[$paramEnd].Content -eq ',' -and $braceCount -eq 1 ) { $theParam = $tokens[$lastParamStart + 1] $paramBody = $text.Substring($theParam.Start, $tokens[$paramEnd - 1].Start + $tokens[$paramEnd - 1].Length - $theParam.Start) $lastVariable = @([Regex]::Matches($paramBody, "\`$([\w-]{1,})"))[-1].Captures[0].ToString() $paramBlocks[$lastVariable] = $paramBody.Trim("<>,") $lastParamStart = $paramEnd + 1 } $paramEnd++ } while ($braceCount -and ($paramEnd -lt $tokens.Count)) if ($lastParamStart -ne $paramStart) { $theParam = $tokens[$lastParamStart + 1] $paramBody = $text.Substring($theParam.Start, $tokens[$paramEnd - 1].Start + $tokens[$paramEnd - 1].Length - $theParam.Start) $lastVariable = @([Regex]::Matches($paramBody, "\`$([\w-]{1,})"))[-1].Captures[0].ToString() $paramBlocks[$lastVariable] = $paramBody.Trim("<>,") } continue } if ($tokens[$i].type -eq 'keyword' -and $tokens[$i].Content -eq 'configuration') { $ConfigurationName = $tokens[$i+1].Content if (-not $Name) { $Name = $ConfigurationName } while ($tokens[$i].type -ne 'GroupStart' -and $tokens[$i].type -ne '{') { $i++ } $braceCount = 0 $j = $i $GroupStart = $tokens[$i] do { if ($tokens[$j].type -eq 'GroupStart' -and $tokens[$j].Content -eq '{') { $braceCount++ } if ($tokens[$j].type -eq 'GroupEnd' -and $tokens[$j].Content -eq '}') { $braceCount-- } $j++ } while ($braceCount -and ($j -lt $tokens.Count)) continue } if ($tokens[$i].type -eq 'command' -and $tokens[$i].Content -eq 'Import-DSCResource') { $null = $null $j = $i + 1 $afterModuleNameParameter = $false $afterNameParameter = $false while ($tokens[$j].Type -ne 'Newline' -and $tokens[$j].Type -ne 'StatementSeparator') { if ($tokens[$j].Type -eq 'CommandParameter') { $afterModuleNameParameter = $tokens[$j].Content -eq '-ModuleName' -or $tokens[$j].Content -eq '-Module' $afterNameParameter = $tokens[$j].Content -eq '-Name' -or $tokens[$j].Content -eq 'ResourceName' } if ($tokens[$j].Type -eq 'CommandArgument' -or $tokens[$j].Type -eq 'string') { if ($afterModuleNameParameter) { $null = $allModulesImported.Add($tokens[$j].Content) } elseif ($afterNameParameter) { $null = $ExplicitResourceNames.Add($tokens[$j].Content) } } $j++ } $i = $j - 1 continue } if ($tokens[$i].type -eq 'keyword' -and $tokens[$i].Content -eq 'node') { $InNode = $tokens[$i + 1].Content while ($tokens[$i].type -ne 'GroupStart' -and $tokens[$i].Content -ne '{') { $i++ } $GroupStart = $null continue } if ($tokens[$i].type -eq 'keyword' -and ($tokens[$i].Content -ne 'node' -and $tokens[$i].Content -ne 'param')) { $DscResourceInUse = $tokens[$i].Content $DscResourceSettingName = $tokens[$i+1].Content while ($tokens[$i].type -ne 'GroupStart' -and $tokens[$i].Content -ne '{') { $i++ } $GroupStart = $tokens[$i] $braceCount = 0 do { if ($tokens[$i].type -eq 'GroupStart' -and $tokens[$i].Content -eq '{') { $braceCount++ } if ($tokens[$i].type -eq 'GroupEnd' -and $tokens[$i].Content -eq '}') { $braceCount-- } $i++ } while ($braceCount) $ConfigurationSettingsBlock = $text.Substring($GroupStart.Start, ($tokens[$i].Start + $tokens[$i].Length) - $GroupStart.Start) $variablesUsed = @(foreach ($_ in [Regex]::Matches($ConfigurationSettingsBlock, "\`$([\w-]{1,})")) { $_.Captures[0].ToString() }) $allVariablesUsed.AddRange($variablesUsed) $ResourceNames += $DscResourceInUse $SettingNames += $DscResourceSettingName " $DscResourceInUse $DscResourceSettingName $configurationSettingsBlock " | Add-Member NoteProperty Node $InNode -PassThru | Add-Member NoteProperty ResourceName "$DscResourceInUse" -PassThru | Add-Member NoteProperty SettingName $DscResourceSettingName -PassThru | Add-Member NoteProperty VariablesUsed $DscResourceSettingName -PassThru continue } } } #endregion Parse All Configurations $configBuilder = New-Object Text.StringBuilder $null = $configBuilder.AppendLine("configuration $Name {") if ($allVariablesUsed) { $paramBody = @(foreach ($variableName in $allVariablesUsed) { if (-not $variableName) { continue } $paramBlocks[$variableName] }) -join (',' + [Environment]::NewLine + (" " * 4)) $null = $configBuilder.AppendLine(" " * 4 + "param($($paramBody.Trim("()").Trim()))") } if ($allModulesImported) { $null = $configBuilder.AppendLine(" " * 4 + "Import-DSCResource -Module '$(($allModulesImported | Select-Object -Unique | Sort-Object) -join "','")'") } foreach ($resourceName in $ExplicitResourceNames) { $null = $configBuilder.AppendLine(" " * 4 + "Import-DSCResource -Name '$resourceName'") } foreach ($partsByNode in $allConfigParts | Group-Object Node) { $indentSize = if ($partsByNode.Name) { 8 $null = $configBuilder.AppendLine(" " * 4 + "node $($partsByNode.Name) {") } else { 4 } foreach ($part in $partsByNode.Group) { $null = $configBuilder.AppendLine(" " * $indentSize + $part.Trim()) } if ($indentSize -eq 8) { $null = $configBuilder.AppendLine(" " * 4 + "}") } } $null = $configBuilder.AppendLine("}") [ScriptBlock]::Create("$configBuilder") } } |