Write DSC Configurations

In this chapter we will explain how to write DSC configuration documents.

Configuration Block

This is the simplest part of your configuration script:

configuration MyConfig {
}

Add Nodes

The next part of the configuration identifies the node or nodes that you plan to create MOFs for. At the simplest level, it can look like this:

configuration MyConfig {
	node DEMOWAP0001 {
	}
}

When I run MyConfig, I’ll get DEMOWAP0001.mof as the output. Of course, this isn’t the most efficient way to create configurations, because you’ll often have multiple computers that need the same configuration settings.

Add a Parameter Block

One way you can start to make your configurations more dynamic is to add input parameters:

configuration MyConfig {
	param(
		[string[]]$ComputerName
	)
	
	node $ComputerName {
	}
}

Now, we could run something like MyConfig -ComputerName DEMOWAP0001,DEMOWAP0002,DEMOWAP0003 and get three MOFs.

Add Settings

Within the Node section, you identify the configuration settings that you want to be integrated into the MOF. Let’s start with something really simple:

configuration MyConfig {
	param(
		[string[]]$ComputerName
	)
	
	node $ComputerName {
	
		WindowsFeature Backups {
			Ensure = 'Present'
			Name = 'Windows-Backup'
		}
		
	}
}

Let’s look at what that is creating:

  • We’ve specified a configuration setting that we’ve named Backups.
  • The setting uses the WindowsFeature DSC resource. This happens to be a native resource on Windows Server computers that have WMF 4 or later installed.
  • Within the setting, we’ve specified two properties. The properties of a setting will vary based on the DSC resource it uses. This particular DSC resource offers Ensure and Name as properties; other resources will be different.
  • We’ve specified values - Present and Windows-Backup - for the properties. Again, the valid values will vary depending on the DSC resource that you’re using.

At the most basic level, this is all a configuration is: a group of settings, in a Node block, in a Configuration block.

Add Basic Logic

Configurations can use PowerShell’s entire scripting language to make decisions. For example:

configuration MyConfig {
	param(
		[string[]]$ComputerName
	)
	
	node $ComputerName {
	
		$features = @('windows-backup','dns-server','dhcp-server')
		ForEach ($feature in $features) {
			WindowsFeature $feature {
				Ensure = 'Present'
				Name = $feature
			}
		}
	}
}

When this is run, it’ll produce a MOF with three Windows features. Notice that we’ve used $feature for the name of the setting as well as using it to identify the feature name. That’s because the name of every setting must be unique within the configuration.

This logic only executes when the configuration is run on your authoring machine. The final MOF won’t contain the ForEach loop; the MOF will contain three static settings. MOFs aren’t scripts - they don’t contain logic. MOFs don’t ask questions and make decisions, for the most part; they just do what they contain.

Add Node-Side Logic

There’s one exception to the “MOFs don’t contain logic” rule, and it’s the special, built-in Script resource.

configuration MyConfig {
	param(
		[string[]]$ComputerName
	)
	
	node $ComputerName {

		Script MyScript {
			GetScript  {}
			TestScript {}
			SetScript  {}
		}
	
	}
}

Within the TestScript script block, you can put whatever you want, provided it returns $True or $False as its only output. If it returns $False, then the target node’s LCM will run whatever’s in the SetScript script block. The GetScript script block should return a hash table that describes the current configuration. GetScript can either return an empty hashtable, or a hashtable with a single key-value pair.
The key must be called “Result”, and the value must be a string that represents the state of the configuration.

The fun thing here is that these script blocks don’t run when you run the configuration and create MOFs. Instead, the PowerShell code in those three script blocks goes right into the MOF as-is. When the MOF is applied to a target node, it’s the LCM on the target node that will run these. So this is a sort of way to make “dynamic” MOFs. Here’s a quick example:

configuration MyConfig {
	param(
		[string[]]$ComputerName
	)
	
	node $ComputerName {

		Script MyScript {
			GetScript  { write @{} }
			TestScript { Test-Path 'c:\Program Files\Example Folder' }
			SetScript  { Mkdir 'c:\Program Files\Example Folder' }
		}
	
	}
}

The GetScript returns an empty hash table; that’s not uncommon. The TestScript will return $True if a folder exists, and $False if it doesn’t (because that’s what Test-Path itself returns). If the folder doesn’t exist, my SetScript block creates the folder.

You need to be a little careful with Script resources. They can be harder to maintain and, if they’re complex, really difficult to troubleshoot.

Document Dependencies

You can help the LCM understand the order in which settings must be applied by using the DependsOn property. This is a common property, available for all DSC resources.

configuration MyConfig {
	param(
		[string[]]$ComputerName
	)
	
	node $ComputerName {
	
		WindowsFeature Backups {
			Ensure = 'Present'
			Name = 'Windows-Backup'
		}
		
		WindowsFeature DHCP {
			Ensure = 'Present'
			Name = 'DHCP-Server'
		}		
		
		CustomDHCPLoader LoadDHCP {
			SourceLocation = \\companyroot\admin\dhcp\sources.xml
			DependsOn = [WindowsFeature]DHCP,[WindowsFeature]Backups
		}
		
	}
}

In this example, the CustomDHCPLoader setting won’t run until after the two WindowsFeature settings are confirmed to be correct.

Running the Configuration

In most cases you’ll actually run the configuration right at the bottom of the script file that the configuration lives in:

MyConfig -ComputerName DEMOWAP0001,DEMOWAP0002,DEMOWAP0003

But in addition to any parameters you define, you also get a handful of free parameters that you can use:

  • -InstanceName isn’t something you’ll probably ever use. It lets you override the ID that DSC uses to manage composite configurations. and it’s honestly best to not mess with it.
  • -OutputPath accepts a folder path in which your MOF files will be created. By default, PowerShell will create a folder named after your configuration (/MyConfig, in the example I’ve used), and put the MOFs in there.
  • -ConfigurationData accepts a hash table of configuration data, which you can use to drive logic decisions within the configuration. This is a big topic, and there’s a whole chapter on it later.

Deploy the MOF

Once you’ve got the MOF authored, you’re ready to deploy it. You might do that in Push mode by using Start-DscConfiguration, or in Pull mode. Remember, with config.XO you only need to uplaod the DSC configuration via frontend and your ready to go.