Install Docker on Windows Server 2016 using DSC

Windows Server 2016 is now GA and it contains some pretty exciting stuff. Chief among them for me is support for containers by way of Docker. So, one of the first things I did was start installing Windows Server 2016 VM’s (Server Core and Nano Server naturally) and installing Docker on them so I could begin experimenting with Docker Swarms and other cool stuff.

Edit: If you’re looking for a DSC configuration for setting up Docker on a Windows 10 Anniversary Edition machine, see the Windows 10 AE section below.

At first I started using the standard manual instructions provided by Docker, but this doesn’t really suit any kind of automation or infrastructure as code methodology. This of course was a good job for PowerShell Desired State Configuration (DSC).

So, what I did was put together a basic DSC config that I could load into a DSC Pull Server and build out lots of Docker nodes quickly and easily. This worked really nicely for me to build out lots of Windows Server 2016 Container hosts in very short order:


If you don’t have a DSC Pull server or you just want a simple script that you can use to quickly configure a Windows Server 2016 (Core or Core with GUI only) then read on.

Note: This script and process is really just an example of how you can configure Docker Container hosts with DSC. In a real production environment you would probably want to use a DSC Pull Server.

Get it Done

Edit: After a suggestion from Michael Friis (@friism) I have uploaded the script to the PowerShell Gallery and provided a simplified method of installation. The steps could be simplified even further into a single line, but I’ve kept them separate to show the process.

Using PowerShell Gallery

On a Windows Server 2016 Server Core or Windows Server 2016 Server Core with GUI server:

  1. Log on as a user with Local Administrator privileges.
  2. Start an Administrator PowerShell console – if you’re using Server Core just enter PowerShell at the command prompt:ss_dockerdsc_console
  3. Install the Install-DockerOnWS2016UsingDSC.ps1 script from the PowerShell Gallery using this command:

    You may be asked to confirm installation of these modules, answer yes to any confirmations.
  4. Run the Install-DockerOnWS2016UsingDSC.ps1 script using:


The script will run and reboot the server once. Not long after the reboot the Docker service will start up and you can get working with containers:


You’re now ready to start working with Containers.

The Older Method (without PowerShell Gallery)

On a Windows Server 2016 Server Core or Windows Server 2016 Server Core with GUI server:

  1. Log on as a user with Local Administrator privileges.
  2. Start an Administrator PowerShell console – if you’re using Server Core just enter PowerShell at the command prompt:ss_dockerdsc_console
  3. Install the DSC Resources required for the DSC configuration by executing these commands:

    You may be asked to confirm installation of these modules, answer yes to any confirmations.
  4. Download the Docker installation DSC script by executing this command:

  5. Run the Docker installation DSC script by executing this command:


The script will run and reboot the server once. Not long after the reboot the Docker service will start up and you can get working with containers:


You’re now ready to start working with Containers.

What the Script Does

In case you’re interested in what the script actually contains, here are the components:

  1. Configuration ContainerHostDsc – the DSC configuration that configures the node as a Docker Container host.
  2. Configuration ConfigureLCM – the LCM meta configuration that sets Push Mode, allows the LCM to reboot the node if required and configures ApplyAndAutoCorrect mode.
  3. ConfigData – a ConfigData object that contains the list of node names to apply this DSC Configuration to – in this case LocalHost.
  4. ConfigureLCM – the call to the Configuration ConfigureLCM to compile the LCM meta configuration MOF file.
  5. Set-DscLocalConfigurationManager – this applies the compiled LCM meta configuration MOF file to LocalHost to configure the LCM.
  6. ContainerHostDsc – the call to the Configuration ContainerHostDsc to compile the DSC MOF file.
  7. Start-DSCConfiguration – this command starts the LCM applying the DSC MOF file produces by the ContainerHostDsc.

The complete script can be found here. Feel free to use this code in anyway that makes sense to you.

What About Windows 10 AE?

If you’re looking for a DSC configuration that does the same thing for Windows 10 Anniversary edition, Ben Gelens (@bgelens) has written an awesome DSC config that will do the trick. Check it out here.


Happy containering!


Tips for HQRM DSC Resources

I’ve spent a fair amount of time recently working on getting some of my DSC Resources (SystemLocaleDsc, WSManDsc, iSCSIDsc and FSRMDsc) accepted into the Microsoft DSC Community Resource Kit. Some are nearly there (SystemLocaleDsc and WSManDsc), whereas others have a way to go yet.

I’ve had one resource already accepted (xDFS) into the DSC Community Resource kit, but this was before the High Quality Resource Module (HQRM) guidelines became available. The HQRM guidelines are a set of standards that DSC modules must meet and maintain to be considered a High Quality Resource Module. Once they meet these requirements they may be eligible to have the ‘x’ moniker removed with ‘Dsc‘ being added to the name.

More information: If you want to read a bit more about the HQRM standards, you can find the HQRM Guidelines here.

Any modules being submitted for inclusion into the DSC Community Resource kit will be expected to meet the HQRM standards. The process of acceptance requires three reviewers from the Microsoft DSC team to review the module.

I thought it might be helpful to anyone else who might want to submit a DSC Resource into the DSC Community Resource kit to get a list of issues the reviewers found with my submissions. This might allow you to fix up your modules before the review process – which will help the reviewers out (they hate having to be critical of your code as much as you do). This enables the submission process to go much faster as well.

More information: If you want to read more about the submission process, you can find the documentation here.

I’ll keep this post updated with any new issues the reviewers pick up. Feel free to ask for clarifications on the issues.

So here is my list of what I have done wrong (so far):

Missing Get-Help Documentation

Every function (public or private) within the DSC resource module must contain a standard help block containing at least a .SYNOPSIS and .PARAMETER block:

This will get rejected:


This is good:


Examples Missing Explanation

All examples in the Examples folder and the must contain an explanation of what the example will do.

This is bad:


This is good:


Old or Incorrect Unit/Integration Test Headers

There is a standard method of unit and integration testing DSC Resources. Your DSC resources should use these methods where ever possible. Any tests should therefore be based on the latest unit test templates and integration test templates. You should therefore ensure your tests are based on the latest practices and contain the latest header.

This is probably the hardest thing to get right if you’re not paying close attention to the current DSC community best practices around testing. So feel free to ask me for help.

This is bad:


This is good:


Incorrect Capitalization of Local Variables

Local variables must start with a lower case letter. I needed to correct this on several occasions.

Note: this is for local variables. Parameter names should start with Uppercase.

This is bad:


This is good:


Spaces around = in Localization Files

In any localization files you should make sure there is a space on either side of the = sign. This greatly improves message readability.

This is bad:


This is good:


Missing code of Conduct in

All modules that are part of the DSC Resource Kit must contain this message in the

This project has adopted the Microsoft Open Source Code of Conduct.
For more information see the Code of Conduct FAQ or contact with any additional questions or comments.

This is bad:


This is good:


Missing Localization file indent

All strings in localization files should be indented.

This is bad:


This is good:



Final Words

There were some other issues raised which I will also document, however I am still in discussion with the DSC team over the best methods to use to solve the issues (specifically the use of InModuleScope in unit tests).

The main thing you can do to help speed this process up and reduce the load on the reviewers however is to implement all the best practices and guidelines listed.

I hope this helps someone out there.


cDFS is dead, long live xDFS

The xDFS DSC resource module has been officially released to the PowerShell Gallery thanks to the awesome review efforts of the Microsoft PowerShell Team. The cDFS DSC Resource has now been unlisted from the PowerShell Gallery. So now is the time to update any DSC configuration scripts to use xDFS.


Important: There were some minor changes to xDFS when it was converted from cDFS. For information on what you’ll need to change to convert to xDFS see my earlier post.


cDFS moving to the PowerShell Team

Just a Friday afternoon heads up – if you’re using the cDFS DSC Resource I created to manage Windows Server Distributed File System (Replication and Namespaces), it has now been accepted into the PowerShell Community resources and will be under the control of the PowerShell Team.

This means that the GitHub source code repository will be moving over to the PowerShell organization in the next few days. This also means that any future releases of this resource module won’t be provided by me as cDFS, but will be released by the PowerShell team as xDFS.

So I recommend that when this happens you switch over to using the xDFS resource. I will put another post up here when the change over officially occurs. The first official release version under the new xDFS name will be 3.0.0.x. I won’t make any further changes or bug fixes to the cDFS resources.

It is also worth noting that as part of this move some minor changes were made to the DSC Resource modules. These are breaking changes and you will most likely need to update any DSC Configurations depending on this, but you would have to do this anyway because of the name change.

The changes are:

  • Resource xDFSRepGroup renamed to xDFSReplicationGroup
  • Resource xDFSRepGroupConnection renamed to xDFSReplicationGroupConnection
  • Resource xDFSRepGroupFolder renamed to xDFSReplicationGroupFolder
  • Resource xDFSRepGroupMembership renamed to xDFSReplicationGroupMembership
  • xDFSReplicationGroupConnection:
    • Changed DisableConnection parameter to EnsureEnabled.
    • Changed DisableRDC parameter to EnsureRDCEnabled.

These changes should only require minor changes to your configuration scripts to implement.

Thanks for reading and have a great Friday~



Install Jenkins using DSC – Part 2

In my previous post I showed how to create a PowerShell script that would install a Jenkins CI Master server onto a Windows Server Core installation. The obvious next step for such a script was to convert it into a DSC configuration file.

In this post I’m assuming WMF 5.0 is installed onto the server that will be converted into a Jenkins Master. You could manage this without WMF 5.0, but you’d need to manually install the DSC Resource modules that the configuration will use.

Once again, the full DSC Configuration script can be found at the end of the post.


You’ll need:

  • A physical or virtual machine running Windows Server 2012 R2 Core (or Full) – it should be a completely clean install with WMF 5.0 installed on it.
  • An administrator login to the server.
  • An internet connection to the server.

Resource Modules

This DSC Configuration requires the use of three DSC Resources:

  • cChoco – this community resource is used to install Chocolatey and Jenkins.
  • xNetworking – this resource is used to configure the networking on the server if required.
  • PSDesiredStateConfiguration – this resource comes with PowerShell by default and is used to provide the Script resource.

The easiest way to install these resource modules is by executing these commands on the Jenkins server:

# Make sure the DSC Resource modules are downloaded
Install-Module -Name cChoco -Force
Install-Module -Name xNetworking -Force

However, if you’re using a Pull server or compiling the DSC MOF on a development machine (rather than the Jenkins node) you would need to use other methods of ensuring the modules are available.

The Configuration Components

The DSC Configuration needs to do the following things:

  • Configure Networking (optional)
  • Install .NET 3.5 Framework
  • Install Chocolatey
  • Install JDK 8
  • Install Jenkins
  • Configure Jenkins Port (optional)

Configure Networking

I like to use the xNetwoking DSC resource to configure the IPv4 and IPv6 settings on the Network adapter to have a static configuration. However, you won’t need to do this if you’re using DHCP or manual configuration. Note, in my case my adapter was called “Ethernet”.

xIPAddress IPv4_1 {
    InterfaceAlias = 'Ethernet'
    AddressFamily  = 'IPv4'
    IPAddress      = ''
    SubnetMask     = '24'
xDefaultGatewayAddress IPv4G_1 {
    InterfaceAlias = 'Ethernet'
    AddressFamily  = 'IPv4'
    Address        = ''
xDnsServerAddress IPv4D_1 {
    InterfaceAlias = 'Ethernet'
    AddressFamily  = 'IPv4'
    Address        = ''
xIPAddress IPv6_1 {
    InterfaceAlias = 'Ethernet'
    AddressFamily  = 'IPv6'
    IPAddress      = 'fd53:ccc5:895a:bc00::14'
    SubnetMask     = '64'
xDefaultGatewayAddress IPv6G_1 {
    InterfaceAlias = 'Ethernet'
    AddressFamily  = 'IPv6'
    Address        = 'fd53:ccc5:895a:bc00::13'
xDnsServerAddress IPv6D_1 {
    InterfaceAlias = 'Ethernet'
    AddressFamily  = 'IPv6'
    Address        = 'fd53:ccc5:895a:bc00::a'

Install .NET 3.5 Framework

Jenkins requires the .NET 3.5 Framework, so I’m going to use the WindowsFeature DSC Resource to install it:

WindowsFeature NetFrameworkCore
    Ensure    = "Present"
    Name      = "NET-Framework-Core"

Install Chocolatey

Next up, I’m going to use the cChocoInstaller resource in the cChoco resource module (available on PowerShell Gallery here) to install the Chocolatey package manager:

# Install Chocolatey
cChocoInstaller installChoco
    InstallDir = "c:\choco"
    DependsOn = "[WindowsFeature]NetFrameworkCore"

Install JDK 8 and Jenkins

The cChocoPackageInstaller resource module is the used to install JDK 8 and Jenkins

# Install JDK8
cChocoPackageInstaller installJdk8
    Name = "jdk8"
    DependsOn = "[cChocoInstaller]installChoco"

# Install Jenkins
cChocoPackageInstaller installJenkins
    Name = "Jenkins"
    DependsOn = "[cChocoInstaller]installChoco"

Configure Jenkins Port

The last step of the configuration is optional. By default Jenkins is configured to listen on port 8080, however I want to change it to 80. So this next part uses the Script resource to change the “–httpPort” setting in the Jenkins.xml file. I use Regex to do this:

# Set the Jenkins Port
Script SetJenkinsPort
	SetScript = {
		Write-Verbose -Verbose "Setting Jenkins Port to $Using:JenkinsPort"
		$Config = Get-Content `
			-Path "${ENV:ProgramFiles(x86)}\Jenkins\Jenkins.xml"
		$NewConfig = $Config `
			-replace '--httpPort=[0-9]*\s',"--httpPort=$Using:JenkinsPort "
		Set-Content `
			-Path "${ENV:ProgramFiles(x86)}\Jenkins\Jenkins.xml" `
			-Value $NewConfig `
		Write-Verbose -Verbose "Restarting Jenkins"
		Restart-Service `
			-Name Jenkins
	GetScript = {
		$Config = Get-Content `
			-Path "${ENV:ProgramFiles(x86)}\Jenkins\Jenkins.xml"
		$Matches = @([regex]::matches($Config, "--httpPort=([0-9]*)\s", 'IgnoreCase'))
		$CurrentPort = $Matches.Groups[1].Value
		Return @{
			'JenkinsPort' = $CurrentPort
	TestScript = {
		$Config = Get-Content `
			-Path "${ENV:ProgramFiles(x86)}\Jenkins\Jenkins.xml"
		$Matches = @([regex]::matches($Config, "--httpPort=([0-9]*)\s", 'IgnoreCase'))
		$CurrentPort = $Matches.Groups[1].Value

		If ($Using:JenkinsPort -ne $CurrentPort) {
			# Jenkins port must be changed
			Return $False
		# Jenkins is already on correct port
		Return $True
	DependsOn = "[cChocoPackageInstaller]installJenkins"

Create the MOF

The final thing to do is download the cChoco and xNetworking DSC Resources,create the MOF and then ask the LCM to apply it:

$ConfigData = @{
    AllNodes =
            NodeName = "LocalHost"

JENKINS_CI -JenkinsPort 80 -ConfigurationData $ConfigData

Start-DscConfiguration -Path .\JENKINS_CI -Wait -Verbose

The Complete DSC Configuration

Here is the complete DSC Configuration file. You just need to copy it to the Server and run it. It will compile the configuration into a MOF and tell the LCM to apply it. Just remember to ensure required DSC Resource modules are installed.

Within five to ten minutes the Jenkins server will be configured and ready to go.

Configuring iSCSI and iSNS with DSC

Several months back I created a DSC Resource for configuring iSCSI Server Targets (including Virtual Disks) as well as iSCSI Initiators using Desired State Configuration (DSC). I created this for several reasons:

  1. I needed a way for LabBuilder to automatically build Scale-Out File Servers (with CSVs).
  2. I needed something to use as an example in my Creating Professional DSC Resources series.
  3. No one else had already created one.

This weekend I decided to add iSNS Server support to the resource – for both the ciSCSIServerTarget and ciSCSIInitiator resources. So with that feature added I thought it might be a good opportunity for me to write a quick blog post on how to use these DSC Resources.

Installing the Resource

You can find the new ciSCSI resource in the PowerShell Gallery.

For those of you using Windows Management Framework 5.0 (or have the PowerShellGet module installed) you can just use the command:

Install-Module -Name ciSCSI

If you don’t have Windows Management Framework 5.0 (and don’t have the PowerShellGet module installed) you will need to download and install the resource from the GitHub Repository.

Using the Resource

If you’d rather just jump right into the resource documentation and examples you can find it here. Otherwise, read on and I’ll cover this resource to configure both an iSCSI Server Target and an iSCSI Initiator. I’ll also show how to register iSCSI Server Targets and Initiators with an iSNS Server.

Important: Although the ciSCSI DSC Resource will work on Windows Management Framework 4.0, these examples require the use of the WaitForAny DSC Resource, which is only available in Windows Management Framework 5.0. This resource is used to ensure that the iSCSI Server Target has been created before trying to connect any iSCSI Initiators to it. The resource could be omitted, but errors will reported by the LCM on the iSCSI Initiator computers if the iSCSI Server Target is not available before the iSCSI Initiator DSC MOF is applied.

The Example Environment

In this example, the DSC Configurations that are being created will refer to the following servers:

  • FS1.CONTOSO.COM – this is the file server that will contain the iSCSI Virtual Disks and iSCSI Server Target.
  • CLUS1.CONTOSO.COM,CLUS2.CONTOSO.COM,CLUS3.CONTOSO.COM – these are the Windows Server 2012 R2 (or Windows Server 2016) Cluster Server nodes that will be connecting to the iSCSI Server Target.
  • ISNS1.CONTOSO.COM – this is a server with the iSNS Server Windows Feature installed on it. The iSNS default domain has been configured on this server already.

The DSC configurations that will be created will create four 128GB dynamic iSCSI Virtual Disks on the D:\ drive of FS1.CONTOSO.COM. An iSCSI Server Target called FS1-Server-Target will be created and the four iSCSI Virtual Disks attached to it.

Configuring the iSCSI Server Target

A DSC configuration that creates an iSCSI Server Target requires the following steps to be performed in the DSC Resource:

  1. Install the iSCSI Target Server Windows Feature (FS-iSCSITarget-Server).
  2. Initialize and physical disks that will be used to store the iSCSI Virtual Disks (optional).
  3. Create the iSCSI Virtual Disks that will be used by the iSCSI Server Target.
  4. Create the iSCSI Server Target and optionally register it with an iSNS Server.

Here is the DSC Configuration:

Important: Note that the TargetName is set to ‘FS1-Server-Target‘, which will automatically configure the Target IQN to ‘’. This is because the Microsoft iSCSI Server Target cmdlets automatically name the Server Target for you using the following format:


This is very important to remember because the iSCSI Initiators use this string to identify the Server Target to connect to.

The rest of the components of this DSC Configuration are self-explanatory as long as you keep in mind the example environment that is being configured.

Configuring the iSCSI Initiator

A DSC configuration for each of the iSCSI Initiators that will connect to the iSCSI Server Target requires the following steps to be performed in the DSC Resource:

  1. Start the Microsoft iSCSI Initiator Service service (MSiSCSI).
  2. Use the WaitForAny WMF 5.0 DSC Resource to wait for the iSCSI Server Target to be created (optional).
  3. Connect the iSCSI Initiator to the iSCSI Server Target and optionally register it with an iSNS Server.

Here is the DSC Configuration for CLUS1.CONTOSO.COM (the configuration for the other nodes would be similar except with different InitiatorPortalAddress values):

Important: We need to make sure the NodeAddress is set to the the Target IQN from the iSCSI Server Target – in this case ‘’.

It is also recommended that you use IP Addresses for the TargetPortalAddress and InitiatorPortalAddress parameters rather than server names, as this will force the iSCSI traffic to use the appropriate network adapter.

The components of this DSC Configuration are self-explanatory as long as you keep in mind the example environment that is being configured.

iSNS Server Configuration

There are a few things to keep in mind when you have your iSCSI DSC Configurations registering with an iSNS Server:

  1. The Default Domain on the iSNS Server should have been created.
  2. If the iSNS Server is not available or contactable by the iSCSI Server Target or Initiator when the DSC Configuration is applied the DSC configuration will not throw an error, but the iSNS Server Address will not be set. However, next time the DSC configuration is applied by the LCM it will try again (and again the next time etc).

Using iSNS Server is completely optional and is mostly used in larger environments with more than twenty iSCSI Server Targets and where the Initiators will be connected to the iSCSI Server Targets manually or where DSC can’t be used on the iSCSI Server Targets.

That is all there is to using this resource to configure a Windows Server 2012 iSCSI SAN using DSC.

Note: I have submitted this DSC Resource to be included in the Microsoft Community DSC Resources project. If it is accepted then the name of the DSC Resource will change from ciSCSI to iSCSI. The resource hasn’t yet been reviewed and I’m not aware of an ETA for it. The old ‘c’ and ‘x’ nomenclature used by DSC Resources is being phased out.

If you need some additional guidance or other specific examples, please feel free to drop a comment on this blog post (or the GitHub repository) and I’ll do my best to help you out.