PowerShell DSC 101 – Getting Started

powershell2-300x300Authoring, Configuring and deploying PowerShell DSC

DSC: Desired State Configuration


In the previous post, I introduced PowerShell DSC at a high level and explained how it works, what it can be used for, and the options for setting up servers.  In this post I’m going to cover the first part of implementation, which includes setting up the environment for authoring and deploying, file and folder structures – basically everything you need to get up and running before we go into the content of the scripts themselves.

Pull Server Environment

In order to build and test the DSC, a Hyper-V environment was created to simulate the production environment being targeted. As well as the two target machines (an App Server and a DB Server), a third server (“S1”) has been provisioned as the DSC repository and Pull Server. All three machines in my scenario are using Windows Server 2012 R2.

S1 has also been configured as a Domain Controller and DNS Server for the test environment, the domain is called “corp.calibre.com”. AppServer and DBServer have both been domain joined.

Note that although DSC does not require servers to be domain joined, the trust relationship provided by a domain makes the security requirements for DSC and PowerShell remoting far simpler: When attempting to copy files from remote shares on an untrusted network, credentials need to be passed from the pull server to the target servers and on to the share to allow access. Once domain joined, this access can be configured statically by allowing domain joined authenticated users to access the share.

A goal of my scenario is to try and reduce the amount of manual steps that have to be carried out on the target servers, so where possible, once the target servers have Windows Server 2012 R2 installed from a disk image and added to the network, everything else will be setup from the S1 server.  As stated in my first post, it would be possible to automate the creation of a target VM itself, from a sysprep’ed standard image, but this post is about demonstrating what you can do with DSC. Note that some overhead is incurred in setting up the Pull Server, but this is a one-off investment, and once setup it will manage and maintain all new servers added to the environment.

My source script has been created to cover both target servers by type rather than by specific machine name, using a filter to manage resources that are specific to one or the other type. I did this to keep the common list of components in one place, rather than maintain two completely separate configurations.

Note that will Push configurations, the node is referred to by computer name – with Pull, a configuration is given a GUID, and a target machine is setup to ask for configuration by GUID. Hence, Pull notifications don’t care about what servers are out there, just what configurations are being requested. This also means that if you provision multiple app servers, for example, and you want them to have the same DSC configuration, you can use the same GUID in the local configuration manager of those servers.

Setting up the development environment on S1

As the development machine, and a new Hyper-V VM, the server needed some configuration applied to enable Remote desktop access, and installing WMF5.0 – the Windows Management Framework (http://www.microsoft.com/en-us/download/details.aspx?id=45883) which installs the latest DSC modules and resources, and the Integrated Scripting Environment (ISE) used to author and run the PowerShell scripts as they were being developed.

Enable RDP to Hyper-V Server

Use “sconfig.exe” from an elevated command prompt, ensure that Remote Desktop Access is turned on

Enable firewall rules for RDP

netsh advfirewall firewall set rule group="remote desktop" new enable=Yes

To enable access to web sites for downloading WMF 5.0 and powershell gallery, disable IE ESC

Use the server manager UI to disable IE Enhanced Security Configuration for users and administrators, or run the following powershell script in an elevated ISE session:

$AdminKey = "HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A7-37EF-4b3f-8CFC-4F3A74704073}" 
$UserKey = "HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A8-37EF-4b3f-8CFC-4F3A74704073}" 
Set-ItemProperty -Path $AdminKey -Name "IsInstalled" -Value 0 
Set-ItemProperty -Path $UserKey -Name "IsInstalled" -Value 0

Download and install WMF5.0 (Requires reboot)

Select Win8.1AndW2K12R2-KB3066437-x64.msu from http://www.microsoft.com/en-us/download/details.aspx?id=48729

To enable workgroup machines to communicate using WINRM on the local network

From an elevated command prompt:

 winrm set winrm/config/client @{TrustedHosts="<local>"}

Setup the Pull server runtime environment

Throughout the pre-requisite document, there are references to various pieces of software that need to be downloaded and installed on the target servers. These should all be downloaded to S1, which will provide the installers as assets to the target servers as and when they need to pull them.

Create an SMB share on S1 to host the assets, this will be called \\S1\DSCSMB

Run the following powershell commands from an elevated ISE session:

New-Item -Path C:\DSCSMB -ItemType Directory  
New-SmbShare -Name DSCSMB -Path C:\DSCSMB -ReadAccess Everyone -FullAccess Administrator -Description "SMB Share for pull DSC"  

Create the folder structure for authoring and building the PowerShell scripts For simplicity’s sake I created the development folder off the root of C:\,

New-Item –Path C:\DSC –ItemType Directory

The directory structure will end up looking like this – the subfolders will be created during the build phase.


The next step is to provision and configure the Pull Server’s web service, so an endpoint is available to the target servers over HTTP/HTTPS to call on their scheduled refresh times. Note that for this PoC, HTTP has been used – again to keep the solution simpler, and alleviate the need to make and install certificates. In a production environment, expect to use HTTPs to ensure communication is secured between Pull server and the targets.

configuration NewPullServer
  param ( [string []]$ComputerName =‘localhost’) 
  Import-DSCResource -ModuleName xPSDesiredStateConfiguration  
  Import-DscResource –ModuleName’PSDesiredStateConfiguration’  
  Node $ComputerName {  
    WindowsFeature DSCServiceFeature  
      Ensure = “Present”  
      Name = “DSC-Service”  

    xDscWebService PSDSCPullServer  
      Ensure = “Present”  
      EndpointName = “PSDSCPullServer”  
      Port = 8080  
      PhysicalPath = “$env: SystemDrive\inetpub\wwwroot\PSDSCPullServer”  
      CertificateThumbPrint = “AllowUnencryptedTraffic”  
      ModulePath = “$env: PROGRAMFILES\WindowsPowerShell\DscService\Modules”  
      ConfigurationPath = “$env:PROGRAMFILES\WindowsPowerShell\DscService\Configuration”  
      State = “Started”  
      DependsOn = “[WindowsFeature] DSCServiceFeature”  

    xDscWebService PSDSCComplianceServer  
      Ensure = “Present”  
      EndpointName = “PSDSCComplianceServer”  
      Port = 9080  
      PhysicalPath = “$env:SystemDrive\inetpub\wwwroot\PSDSCComplianceServer”  
      CertificateThumbPrint = “AllowUnencryptedTraffic”  
      State = “Started”  
      IsComplianceServer = $true  
      DependsOn = (“[WindowsFeature] DSCServiceFeature”,”[xDSCWebService]  
#This line actually calls the function above to create the MOF file.  
NewPullServer –ComputerName s1

Execute the above .ps1 script

This will configure 2 end points required by the DSC framework:

PSDSCPullServer – for retrieving the configuration, checksum and any assets required for deployment
PSDSCComplianceServer – to enable remote servers to check compliance of current configuration against desired state

The endpoints can be seen using the IIS Manager tool – as per the screenshot below, you will notice two new web sites configured on ports 8080 and 9080.


Now the pull server is in the correct state for responding to configuration and deployment requests. The following folders will be accessed by the pull server mechanism to retrieve data:

Configuration and checksum files (*.mof, *.mof.checksum) C:\Program Files\WindowsPowerShell\DscService\Configuration
Non Standard DSC Resources (Powershell modules to copy to target) (*.zip, *.zip.checksum) C:\Program Files\WindowsPowerShell\DscService\Modules
Assets (installer files, other files you need to copy to the target) C:\DSCSMB (as \\S1\DSCSMB – defined in the source scripts)

The files in C:\DSC are just where we’re editing the source scripts, the output (mof and checksum files) will be created in subfolders according to the name of the configuration. The DSC itself consists of a single .ps1 configuration for both nodes, and a .psd (data) parameters file to specify the machines within the environment. If you want then to provision and manage multiple app servers, for example, you would add them as a node to the .psd1 file, then run the configuration script again, which will produce a .MOF file per node.

As part of simplifying/automating the build process, there is a “publish.ps1” script included, which will run the configuration against the data file, create the checksums for the .MOF files, then copy the MOF and checksums to the output folder, ready to be pulled by the target servers.

authoring flow

The environment config.psd1 file specifies what machines make up the environment being built, and contains global configuration parameters (used by custom scripts) – S1

    AllNodes = 
            NodeName = "S1";
            Role     = "PullServer"

            NodeName = "AppServer";
            Role     = "AppServer"

            NodeName = "SQLServer";
            Role     = "SQLServer"

    NonNodeData =
        PackagePath = "\\s1\dscsmb";   
        TempPath = "c:\windows\temp" 

The configuration itself is a single file with the Configuration keyword defined our desired state.

The structure consists of four main sections:

  • Import-DscResource keywords for importing additional DscResource Modules
  • A configuration block selecting All Nodes (Node $AllNodes.NodeName) – no filtering – this will include configuration that’s common to all servers
  • A configuration block for the App Server nodes (Node $AllNodes.Where{$_.Role –contains “AppServer”}.NodeName)
  • A configuration block for the SQL Server Nodes (Node $AllNodes.Where{$_Role –contains “SQLServer”}.NodeName)

The next post in this series describes the mechanism for implementing each type of configuration state per the requirements document.

  • Hi Russell, thanks for the excellent post. Your last bullet point has a typo:

    A configuration block for the SQL Server Nodes (Node $AllNodes.Where($_Role –contains “SQLServer”}.NodeName)

    Where(…} => your openning bracket should be a curly bracket instead of round.

    • youngr6

      Many thanks Christos, good spot! Fixed.