Skip to content

Commit 595c875

Browse files
author
Mike Walker
committed
Initial commit
0 parents  commit 595c875

File tree

10 files changed

+204
-0
lines changed

10 files changed

+204
-0
lines changed

.vscode/settings.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Place your settings in this file to overwrite default and user settings.
2+
{
3+
"editor.rulers": [
4+
100
5+
],
6+
"editor.wrappingColumn": 100
7+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
<#
2+
.SYNOPSIS
3+
Generate and optionally apply DSC Local Meta Configuration
4+
.DESCRIPTION
5+
Generate and optionally apply DSC Local Meta Configuration
6+
.EXAMPLE
7+
C:\PS> New-DscMetaConfiguration -Apply -ConfigurationMode ApplyOnly -RefreshMode Push -RebootNodeIfNeeded $false -DebugMode None -ActionAfterReboot StopConfiguration -StatusRetentionTimeInDays 30
8+
.EXAMPLE
9+
C:\PS> New-DscMetaConfiguration -Path c:\temp\mykewlmeta.mof -ConfigurationMode ApplyOnly -RefreshMode Push -RebootNodeIfNeeded $false -DebugMode None -ActionAfterReboot StopConfiguration -StatusRetentionTimeInDays 30
10+
#>
11+
function New-DscMetaConfiguration
12+
{
13+
[CmdletBinding()]
14+
param
15+
(
16+
[parameter(Mandatory = $true, Position = 0, ParameterSetName = 'file')]
17+
[string]$Path,
18+
[parameter(Mandatory = $true, Position = 0, ParameterSetName = 'apply')]
19+
[switch]$Apply,
20+
21+
[ValidateSet('ContinueConfiguration', 'StopConfiguration')]
22+
$ActionAfterReboot,
23+
[string]$CertificateId,
24+
[ValidateSet('ApplyOnly', 'ApplyAndMonitor', 'ApplyAndAutoCorrect')]
25+
[string]$ConfigurationMode,
26+
[int]$ConfigurationModeFrequencyMins,
27+
[ValidateSet('None', 'ForceModuleImport', 'All')]
28+
[string]$DebugMode,
29+
[bool]$RebootNodeIfNeeded,
30+
[ValidateSet('Disabled', 'Push', 'Pull')]
31+
[string]$RefreshMode,
32+
[int]$RefreshFrequencyMins,
33+
[int]$StatusRetentionTimeInDays
34+
)
35+
36+
$params = $PSBoundParameters
37+
$params.Remove('Path') | Out-Null
38+
$params.Remove('Apply') | Out-Null
39+
40+
$metaMofTemplate = @"
41+
[DSCLocalConfigurationManager()]
42+
configuration LCMConfig
43+
{
44+
Node localhost
45+
{
46+
Settings
47+
{
48+
%Settings%
49+
}
50+
}
51+
}
52+
"@
53+
54+
$tabLevelMatch = (($metaMofTemplate -split "`n") | Select-String -Pattern '(\s+)%Settings%')
55+
$tabLevel = $tabLevelMatch.Matches.Captures.Groups[1].Value
56+
[string[]]$replacementLines = @()
57+
foreach($param in $params.GetEnumerator())
58+
{
59+
$value = "'$($param.Value)'"
60+
if($param.Value -is [int])
61+
{
62+
$value = "$($param.Value)"
63+
}
64+
if($param.Value -is [bool])
65+
{
66+
$value = "`$$($param.Value)"
67+
}
68+
$replacementLines += "$tabLevel$($param.Key) = $value"
69+
}
70+
71+
$metaMof = $metaMofTemplate -replace "$($tabLevelMatch.Matches.Captures.Groups[0].Value)", ($replacementLines -join "`r`n")
72+
73+
$tempRoot = [IO.Path]::GetTempPath()
74+
$tempFolder = Get-Date -Format FileDateTime
75+
$tempPath = Join-Path -Path $tempRoot -ChildPath $tempFolder
76+
New-Item -Path $tempRoot -Name $tempFolder -ItemType Directory | Out-Null
77+
Write-Verbose 'Generating DSC meta configuration...'
78+
Push-Location
79+
Set-Location $tempPath
80+
Invoke-Expression $metaMof
81+
LCMConfig | Out-Null
82+
83+
if($PSCmdlet.ParameterSetName -eq 'file')
84+
{
85+
Write-Verbose "Writing DSC meta configuration to $Path..."
86+
Copy-Item -Path "$tempPath\LCMConfig\localhost.meta.mof" -Destination $Path | Out-Null
87+
Pop-Location
88+
Remove-Item -Path $tempPath -Recurse -Force | Out-Null
89+
return Get-Item -Path $Path
90+
}
91+
92+
if($PSCmdlet.ParameterSetName -eq 'apply')
93+
{
94+
Write-Verbose 'Applying DSC meta configuration...'
95+
Set-DscLocalConfigurationManager -Path "$tempPath\LCMConfig"
96+
Pop-Location
97+
Remove-Item -Path $tempPath -Recurse -Force | Out-Null
98+
}
99+
}

ApplyMetaMof/readme.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Summary
2+
=======
3+
The `New-DscMetaConfiguration` cmdlet allows you to create a _localhost.meta.mof_ with the options specified when calling the cmdlet. Optionally, it will also apply the the meta configuration (and remove the temporary _localhost.meta.mof_).
4+
5+
How To Run
6+
==========
7+
Dot source the `New-DscMetaConfiguration.ps1` file into your session and run the following:
8+
9+
```powershell
10+
New-DscMetaConfiguration -Path c:\temp\myTemp.mof -ConfigurationMode ApplyOnly -RefreshMode Push -RebootNodeIfNeeded $false -DebugMode None -ActionAfterReboot StopConfiguration -StatusRetentionTimeInDays 30
11+
```
12+
13+
How It Works
14+
============
15+
At it's core, this cmdlet just builds the DSC configuration code to create a _localhost.meta.mof_ file, based on the parameters specified. From there, a meta configuration file can be created, or it can be directly applied.
16+
17+
Future
18+
======
19+
In the near future, we will be packaging this cmdlet into a module and publishing to the PowerShell Gallery.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Must be same as filename
2+
Configuration SubConfiguration
3+
{
4+
param
5+
(
6+
[Hashtable]$Node
7+
)
8+
9+
Write-Verbose "Verbose output from SubConfiguration.ps1"
10+
11+
File Test1
12+
{
13+
SourcePath = 'C:\test.txt'
14+
DestinationPath = 'C:\test2.txt'
15+
Ensure = 'Present'
16+
}
17+
18+
# If the node is not a 'web' role server, then do not run anything after the next line
19+
# (it won't be added to the MOF file)
20+
if($Node.Roles -notcontains 'web') { return }
21+
22+
File Test2
23+
{
24+
SourcePath = 'C:\test10.txt'
25+
DestinationPath = 'C:\test12.txt'
26+
Ensure = 'Present'
27+
}
28+
}

NestedConfigs/Output/test1.mof

3.06 KB
Binary file not shown.

NestedConfigs/Output/test2.mof

3.06 KB
Binary file not shown.

NestedConfigs/Output/test3.mof

2.15 KB
Binary file not shown.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
$pref = $VerbosePreference
2+
$VerbosePreference = 'continue'
3+
4+
Configuration RootConfiguration
5+
{
6+
# Get all ps1 files in the current folder except this one
7+
$configsToProcess = Get-ChildItem -Path "$($PSScriptRoot)\Configurations" -Filter '*.ps1'
8+
9+
Import-DscResource -ModuleName 'PSDesiredStateConfiguration' # Import statements must go in the central/root configuration
10+
11+
# Start going over each node
12+
Node $AllNodes.NodeName
13+
{
14+
foreach($config in $configsToProcess)
15+
{
16+
Write-Verbose "Processing sub-configuration $($config.BaseName)..."
17+
. "$($config.FullName)" # Import script
18+
. "$($config.BaseName)" -Node $Node # Invoke configuration
19+
Write-Verbose "Finished processing sub-configuration $($config.BaseName)."
20+
}
21+
}
22+
}
23+
24+
$data = @{ AllNodes =
25+
@(
26+
@{ NodeName = 'test1'; Roles = 'web'; Property1 = $true },
27+
@{ NodeName = 'test2'; Roles = 'web'; Property1 = $false },
28+
@{ NodeName = 'test3'; Roles = 'db'; Property1 = $true }
29+
)}
30+
31+
RootConfiguration -ConfigurationData $data -OutputPath "$PSScriptRoot\Output"
32+
$VerbosePreference = $pref

NestedConfigs/readme.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
Summary
2+
=======
3+
This example shows how configurations can be nested. Nesting configurations in this way, along with utilizing node configuration data, can allow you to build a group of configurations that selectively adds certain components, based on the configuration data.
4+
5+
How To Run
6+
==========
7+
This example can be run by cloning this repository, and then executing `RootConfiguration.ps1`. MOF files will be created in `\NestedConfigs\Output`.
8+
9+
How It Works
10+
============
11+
The `RootConfiguration.ps1` is the primary entry-point configuration. Executing this configuration will look for all `.ps1` files in the _Configurations_ folder, and will dynamically import them and execute the sub-configurations. It is required that sub-configurations have the same file name as their configuration name. This is used as a convention so that `RootConfiguration.ps1` knows which configuration to call.
12+
13+
Since each sub-configuration has access to the `$Node` variable, you can selectively skip configuration blocks (or return from the entire sub-configuration to return back to the root configuration) to change the resulting MOF file on a per-node basis.
14+
15+
Each node is processed consecutively, and the set of sub-configurations is processed for each node.

readme.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Table Of Contents
2+
=================
3+
1. ApplyMetaMof
4+
2. NestedConfigs

0 commit comments

Comments
 (0)