Desired State Configuration (DSC) automates administrations and make it less an hassle, it decreases the complexity of scripting in windows and Linux systems and increase the speed of iteration, it is basically configuration as code (Infrastructure As Code). This feature is available beginning the release of powershell version 4.

Index

Why DSC configuration ?

When our golden system configuration(s) break or change, most of the time we find ourself asking “What did change?” we spend countless hours trying to figure out what have changed, this is where PowerShell Desired State Configurations become handy, DSC can ENSURE that our system has the correct configurations no matter what by preventing configurations drift.

The main advantages of DSC are:

  • Simplyfying system admnistrators task (A system admin, can for example configured one or more devices automatically)
  • Identical configurations everywhere (Very handy for standardisation)

DSC Structure

A configuration is nothing more than a special kind of powershell function, the following example shows the anatomy of a basic DSC configuration.

Configuration myconfig
{
   Import-DscResource -Module ModuleName
   Node mynode
   {
      WindowsFeature myrole
      {
          Ensure      = "Present"
          Name        = "Web-Server"  
      }
   }
}

Explanation of each block in details.

Key Components of DSC

When working with dsc you will become very familiar with the following terms such as:

  • DSC Configurations
  • DSC Resources
  • LCM
  • Pull Model
  • Push Model

DSC Configurations

Configurations are codes that define what the resources should do, they consists of the following parts:

  • One Configuration block.
  • One or more Node blocks.
  • One or more resource blocks.

The Configuration block

Any Desired State Configuration document script starts with the word Configuration followed by a desired name then a script block {...}, the following example shows myconfig acting as the name of a dsc configuration, the name can be any name you want it to.

Configuration myconfig
{
   
}

the second example shows Enceladus as the name of the configuration.

Configuration Enceladus
{
   
}

Congratulations! You have your first configuration block, now what’s next? Well as you can notice, both our configuration block myconfig and Enceladus are empty, let’s change that by importing resources from a module into the DSC config.

To do that we use the Import-DscResource cmdlet following the module name containing the resources we want to use, in that case the PSDesiredStateConfiguration module, now you must be wondering, how do we know which module contains the right resources to use? Good question, please keep reading.

Configuration Enceladus
{
   Import-DscResource -Module PSDesiredStateConfiguration
   
}

Ok, we now know how to import resources from a module into a configuration block, great but i want you to understand more so, let’s dive into the details of dsc resources.

DSC Resources

The best way to give you a good understanding of dsc resources is to create a real world scenario, here is one, create a dsc configuration named Enceladus purpose is to make sure the windows service BITS status is stopped and stays stopped on the local machine.

Here is what we know about this request so far:

  • Name of the configuration (Enceladus)
  • Name of the target Node (localmachine)

Here is what we don’t know:

  • Name of the module containing the resource to be used (…)
  • Name of the Resource (…)
  • Name of the Resource properties (…)

Windows PowerShell has a built-in function named Get-DscResource when executed, retrieves all the PowerShell DSC resources present on the computer, if you’ve never worked before with DSC, then when you execute the Get-DscResource command you mostly will be welcomed with the PowerShell in-box resources shown in the output result. Now of course they might not be enough for the needs of your company, but don’t worry there is plethora of Custom DSC Resources from Microsoft and Other vendors you can use or you can even create your own DSC Resources. We will talk about how to create, where to get community dsc resources and how to import them in a dedicated post, but if you want to explorer them already the PowerShell team at Microsoft has a git repo https://github.com/powershell/dscresources with some official resource kit modules or visit the www.powershellgallery.com for community-submited DSC resource modules.

Ok enough, let’s run this command to gather available local DSC resource.

Command:

Get-DscResource

Output: certain values were omitted for the sake of displaying not too much data, yours might look slightly different.

ImplementedAs   Name                      ModuleName                     Version    Properties
-------------   ----                      ----------                     -------    ----------
PowerShell      PSRepository              PowerShellGet                  2.2.1      {Name, DependsOn, Ensure, InstallationPolicy...}
PowerShell      Registry                  PSDesiredStateConfiguration    1.1        {Key, ValueName, DependsOn, Ensure...}
PowerShell      Script                    PSDesiredStateConfiguration    1.1        {GetScript, SetScript, TestScript, Credential...}
PowerShell      Service                   PSDesiredStateConfiguration    1.1        {Name, BuiltInAccount, Credential, Dependencies...
Composite       ServiceSet                PSDesiredStateConfiguration    1.1        {DependsOn, PsDscRunAsCredential, Name, Startup...
PowerShell      User                      PSDesiredStateConfiguration    1.1        {UserName, DependsOn, Description, Disabled...}
PowerShell      WindowsProcess            PSDesiredStateConfiguration    1.1        {Arguments, Path, Credential, DependsOn...}
PowerShell      Archive                   PSDscResources                 2.12.0.0   {Destination, Path, Checksum, Credential...}

Now if we look closely to the output, we can quickly notice that the Name column has a resource called Service and the ModuleName column explicitely tell us the module name the resource belongs to, in that case it’s belong to the PSDesiredStateConfiguration module, which is one of the built-in Windows Powershell module that reside in "${env:SystemRoot}\System32\WindowsPowerShell\v1.0\Modules" folder.

ImplementedAs   Name                      ModuleName                     Version    Properties
-------------   ----                      ----------                     -------    ----------                                                                                                            
PowerShell      Service                   PSDesiredStateConfiguration    1.1        {Name, BuiltInAccount, Credential, Dependencies...

Now to make sure that we can use the recently discovered Service resource for our scenario, let’s retrieve all it’s properties with the following command:

Command

Get-DscResource -Name "Service" -Module "PSDesiredStateConfiguration" | Select-Object -ExpandProperty Properties

or

(Get-DscResource -Name "Service" -Module "PSDesiredStateConfiguration").Properties

Output:

Name                 PropertyType   IsMandatory Values
----                 ------------   ----------- ------
Name                 [string]              True {}
BuiltInAccount       [string]             False {LocalService, LocalSystem, NetworkService}
Credential           [PSCredential]       False {}
Dependencies         [string[]]           False {}
DependsOn            [string[]]           False {}
Description          [string]             False {}
DisplayName          [string]             False {}
Ensure               [string]             False {Absent, Present}
Path                 [string]             False {}
PsDscRunAsCredential [PSCredential]       False {}
StartupType          [string]             False {Automatic, Disabled, Manual}
State                [string]             False {Running, Stopped}

Great! we have a promising output, notice that in the Name column there is a property called State it is NotMandatory and it can only be assigned one of those two values {Running or Stopped} their PropertyType should be a string, in the other hand if you look at the Name property in the Name column this one IsMandatory and only accepts a string as PropertyType.

Whenever you notice the IsMandatory set to True that is mean in order to use the resource, we need to set the mandatory property into our configuration.

Understand the nested blocks (the node block and the resource block)

The Node block

The Node block is where you target the machine that the configuration will be applied to, it is always nested within the configuration block. You write the node block with the word Node following the name of the machine , then a script block {...}

Configuration Enceladus
{
    Import-DscResource -Module PSDesiredStateConfiguration
    Node $env:COMPUTERNAME
    {
 
    }
}


The Resource block

This block starts first wirh the name of the resource, it tells the configuration which one to use from the imported DSC module, it is always nested within the Node block, for example in the following scenario we used the Service resource, service is it’s name and it is a resource in the PSDesiredStateConfiguration module then we give it a descriptive name of EnceladusBits this name can be anything you want, then right after that a script block {...}

Configuration Enceladus
{
    Import-DscResource -Module PSDesiredStateConfiguration
    Node $env:COMPUTERNAME
    {
        Service EnceladusBits
        {

        }
    }
}

Now let’s write our final configuration based on what we have learn so far, so the scenario was to create a dsc configuration named Enceladus, purpose was to make sure the windows service BITS status was stopped and stay stopped on the local machine.

Here is everything we gathered and know:

  • Name of the configuration (Enceladus)
  • Name of the module containing the resource to be used (PSDesiredStateConfiguration)
  • Name of the target Node (localmachine)
  • Name of the Resource (Service)
  • Name of the Resource properties we need (Name, State)
Configuration Enceladus
{
    Import-DscResource -Module PSDesiredStateConfiguration
    Node $env:COMPUTERNAME
    {
        Service EnceladusBits
        {
            Name  = "BITS"
            State = "Stopped"
        }
    }
}

You should be pretty confident by now on writing your first dsc configuration, now let’s test what you have learn.

Challenge: C01 Write a dsc configuration named Europa that will always set your local machine computer description to: This is my automation machine.. The resource you will use, when found, give it a descriptive name of EuropaDescription.

  • Name of the configuration (Europa)
  • Name of the module containing the resource to be used ()
  • Name of the target Node (localmachine)
  • Name of the Resource ()
  • Name of the Resource description (EuropaDescription)
  • Name of the Resource properties we need ()

You are so far learning the basics and we will keep doing so until part 3 of this article, it is really important that you know how everything works before diving into some more advanced DSC concepts. I recommend first to solve the challenge before revealing it, thus will ensure you understand what we have discussed so far.

Result: (reveal C01)

DSC Configuration phases

Before we go further, let’s talk about the phases your configuration named Europa will go through, this is very important to understand. So grab a cup of coffee or tea and let’s dive in.There are two phases you will be dealing with when working with DSC Configuration:

  • The Authoring and Staging Phase
  • The Enacting and Reporting Phase

We will dive into each phase.

The Authoring and Staging Phase: This phase is all about preparations, this is where we can write configuration documents that explicitly define what and how the resources should be configured on the targeted system(s), as a matter of fact since the beginning of this article we have been using the authoring and the staging phase.

The Enacting Phase: This phase is when we apply the staged configuration to the targeted system(s) workstations or servers

The Reporting Phase: Ok! you’ve staged your configuration and applied it, but of course you want to keep track for any configuration drift, this is where the reporting phase becomes handy.


We have authored already a Configuration named Europa in our (reveal C01) challenge, let’s author and staged a new one to demonstrate the phases.

Here is our new scenario:

Scenario Author a DSC configuration named Cassini on your localmachine that will ensure Windows Update Service is running and start up type is automatic give the resource name a descriptive name of WindowsUpdate

Let’s go through all the phases with this scenario.

The Authoring and Staging Phase

Phase 1: Authoring and Staging

Configuration Cassini
{
    Import-DscResource -Module PSDesiredStateConfiguration
    Node $env:COMPUTERNAME
    {
        Service WindowsUpdate
        {
            Name        = "wuauserv"
            State       = "Running"
            StartupType = "Automatic"
        }
    }
}

The next phase will be the Enacting Phase, you must know that the Cassini configuration we just authored need to be compiled first before we can Enact (apply) it. When you compile a DSC Configuration, it creates MOF(Managed Object Format) file.

Compile a DSC Configuration

To do the compiling it is very simple, just write the name of the configuration in our case it is Cassini follow by the -OutPath and -Verbose parameter, the verbose parameter is not mandatory but i suggest you put it so you can see what’s happening in the compiling process, before you start compiling, you need first to load your configuration into memory, you can check the Function drive to confirm if its there or not. Remember that a i said a DSC Configuration after all is nothing more than a special kind of PowerShell function, so this is why we look for it into this drive.

Command:

 Get-ChildItem Function:

Output:

CommandType     Name                                    Version    Source
-----------     ----                                    -------    ------
Function        BuildResourceCommonParameters           1.0        WindowsOptionalFeatureSet
Function        BuildResourceString                     1.0        WindowsOptionalFeatureSet
Configuration   Cassini
Function        Configuration                           1.1        PSDesiredStateConfiguration
Function        Connect-TO                              1.0.0.0    CYB.PowerShellProfile.ISE
Function        ConvertFrom-SddlString                  3.1.0.0    Microsoft.PowerShell.Utility
Function        Disable-DscDebug                        1.1        PSDesiredStateConfiguration

Notice something? you can see in the output above that the Cassini configuration is loaded into the function drive and ready to be compiled. Now you can run the following command to compile.

Command:

Cassini -OutputPath "C:\temp\Demo\DSC" -Verbose

This will generated the MOF file containing my config in the C:\temp\Demo\DSC folder.

Output:

    Directory: C:\temp\Demo\DSC


Mode         LastWriteTime          Length   Name                                                                                                                                                                                                             
----         -------------------    ------   ----                                                                                                                                                                                                             
-a----       08-Jun-20     17:04    1886     CYB00356.mof

Congratulation! you’ve compiled your first DSC Configuration, notice the generated CYB00356.mof file, since we explicitely set the target node to be the localhost $env:COMPUTERNAME in the Node block in the DSC configuration, then the compiled MOF file name will be the name of the target_node_name.MOF

If when you ran your command line you welcome with an error saying ‘Cassini : The term ‘Cassini’ is not recognized as the name of a cmdlet, function, script file, or operable program.’ then that’s mean the configuration was not loaded in memory to fix this, select with your mouse your authored configuration and load it into memory and then re-run your command.

The Enacting and Reporting Phase

We have authored, staged and compiled, now it is time to finally enact our first DSC Configuration, but before we do so let’s take a look inside the generated CYB00356.mof file, we want to know about the structure of this file, what is in there.

CYB00356.mof Content

/*
@TargetNode='CYB00356'
@GeneratedBy=CELESTIN
@GenerationDate=06/08/2020 17:04:13
@GenerationHost=CYB00356
*/

instance of MSFT_ServiceResource as $MSFT_ServiceResource1ref
{
ResourceID = "[Service]WindowsUpdate";
 State = "Running";
 SourceInfo = "::7::9::Service";
 Name = "wuauserv";
 StartupType = "Automatic";
 ModuleName = "PSDesiredStateConfiguration";

ModuleVersion = "1.0";

 ConfigurationName = "Cassini";

};
instance of OMI_ConfigurationDocument


                    {
 Version="2.0.0";
 

                        MinimumCompatibleVersion = "1.0.0";
 

                        CompatibleVersionAdditionalProperties= {"Omi_BaseResource:ConfigurationName"};
 

                        Author="CELESTIN";
 

                        GenerationDate="06/08/2020 17:04:13";
 

                        GenerationHost="CYB00356";
 

                        Name="Cassini";


                    };

Once you generated the MOF file this where the authoring process ends, now you are thinking more about applying the configuration, the enacting process. This CYB00356.mof file that we compiled can be stored either locally or on a pull server, either an SMB share or a DSC pull service repsoitory.

Now understand this, when you author a configuration locally like we did, you can apply this configuration by Pushing it to a target node in our case we will do so to the local machine. But also keep in mind that when you author a configuration on a DSC Pull server, then it is the target node job to Pull the configuration from the pull server and enact (apply) it.

This lead me to talk about the two Model of DSC Configuration:

DSC Configuration Models

There are two model of DSC:

Push Model

Push mode refers to a user actively applying a configuration to a target node by calling the Start-DscConfiguration cmdlet.

pushmode

Pull Model

In pull mode, pull clients are configured to get their desired state configurations from a remote pull service.

pullmode

You must understand now that we will have to push our configuration to the localmachine since we did not set up any dsc pull service, we just did author a config and compiled it that’s generated the MOF, now we need to PUSH it to the target node, which is localhost.

We have a set of DSC commands that help in the configuration staging, enacting and reporting, they are exported from the PSDesiredStateConfiguration the DSC built-in module.

Command:

Get-Command -Module "PSDesiredStateConfiguration"

Output:

CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Function        Configuration                                      1.1        PSDesiredStateConfiguration
Function        Disable-DscDebug                                   1.1        PSDesiredStateConfiguration
Function        Enable-DscDebug                                    1.1        PSDesiredStateConfiguration
Function        Get-DscConfiguration                               1.1        PSDesiredStateConfiguration
Function        Get-DscConfigurationStatus                         1.1        PSDesiredStateConfiguration
Function        Get-DscLocalConfigurationManager                   1.1        PSDesiredStateConfiguration
Function        Get-DscResource                                    1.1        PSDesiredStateConfiguration
Function        New-DscChecksum                                    1.1        PSDesiredStateConfiguration
Function        Remove-DscConfigurationDocument                    1.1        PSDesiredStateConfiguration
Function        Restore-DscConfiguration                           1.1        PSDesiredStateConfiguration
Function        Stop-DscConfiguration                              1.1        PSDesiredStateConfiguration
Cmdlet          Invoke-DscResource                                 1.1        PSDesiredStateConfiguration
Cmdlet          Publish-DscConfiguration                           1.1        PSDesiredStateConfiguration
Cmdlet          Set-DscLocalConfigurationManager                   1.1        PSDesiredStateConfiguration
Cmdlet          Start-DscConfiguration                             1.1        PSDesiredStateConfiguration
Cmdlet          Test-DscConfiguration                              1.1        PSDesiredStateConfiguration
Cmdlet          Update-DscConfiguration                            1.1        PSDesiredStateConfiguration


I will explain in details each command in the upcoming Part II of this article, for now let’s just enact(apply) our configuration for the sake of showing you how to complete the The Enacting and Reporting Phase.

The Start-DscConfiguration command is the one that we will use to apply or enact the Cassini configuration, we give it the -Path parameter, bear in mind that the path should the location where you compiled the Cassini configuration. In my case i compiled the MOF in C:\temp\Demo\DSC folder and also notice that i use the -Wait and the -Verbose parameter to get some output of what is happening.

As i said earlier, in the upcoming Part II of this article, i will explain in details what each command does, for now just know that the Start-DscConfiguration command all it does is grab the CYB00356.mof file we did compiled earlier and PUSH it to the target node as pending configuration and immediately apply it.

Command:

Start-DscConfiguration -Path "C:\temp\Demo\DSC" -Wait -Verbose

Output:

VERBOSE: Perform operation 'Invoke CimMethod' with following parameters, ''methodName' = SendConfigurationApply,'className' = MSFT_DSCLocalConfigurationManager,'namespaceName' = root/Microsoft/Windows/DesiredStateConfiguration'.
VERBOSE: An LCM method call arrived from computer CYB00356 with user sid S-1-5-21-9373513-708069497-1238792691-1001.
VERBOSE: [CYB00356]: LCM:  [ Start  Set      ]
VERBOSE: [CYB00356]: LCM:  [ Start  Resource ]  [[Service]WindowsUpdate]
VERBOSE: [CYB00356]: LCM:  [ Start  Test     ]  [[Service]WindowsUpdate]
VERBOSE: [CYB00356]:                            [[Service]WindowsUpdate] Perform operation 'Query CimInstances' with following parameters, ''queryExpression' = SELECT * FROM Win32_Service WHERE Name='wuauserv','queryDialect' = WQL,'namespaceName' = root\cimv2'.
VERBOSE: [CYB00356]:                            [[Service]WindowsUpdate] Operation 'Query CimInstances' complete.
VERBOSE: [CYB00356]:                            [[Service]WindowsUpdate] Startup type for service 'wuauserv' is 'Manual'. It does not match 'Automatic'.
VERBOSE: [CYB00356]: LCM:  [ End    Test     ]  [[Service]WindowsUpdate]  in 2.1400 seconds.
VERBOSE: [CYB00356]: LCM:  [ Start  Set      ]  [[Service]WindowsUpdate]
VERBOSE: [CYB00356]:                            [[Service]WindowsUpdate] Service 'wuauserv' already exists. Write properties such as Status, DisplayName, Description, Dependencies willbe ignored for existing services.
VERBOSE: [CYB00356]:                            [[Service]WindowsUpdate] Perform operation 'Query CimInstances' with following parameters, ''queryExpression' = SELECT * FROM Win32_Service WHERE Name='wuauserv','queryDialect' = WQL,'namespaceName' = root\cimv2'.
VERBOSE: [CYB00356]:                            [[Service]WindowsUpdate] Operation 'Query CimInstances' complete.
VERBOSE: [CYB00356]:                            [[Service]WindowsUpdate] Perform operation 'Invoke CimMethod' with following parameters, ''instance' = Win32_Service: Windows Update (Name = "wuauserv"),'methodName' = Change,'namespaceName' = root/cimv2'.
VERBOSE: [CYB00356]:                            [[Service]WindowsUpdate] Operation 'Invoke CimMethod' complete.
VERBOSE: [CYB00356]:                            [[Service]WindowsUpdate] Service 'wuauserv' already started, no action required.
VERBOSE: [CYB00356]: LCM:  [ End    Set      ]  [[Service]WindowsUpdate]  in 1.0230 seconds.
VERBOSE: [CYB00356]: LCM:  [ End    Resource ]  [[Service]WindowsUpdate]
VERBOSE: [CYB00356]: LCM:  [ End    Set      ]
VERBOSE: [CYB00356]: LCM:  [ End    Set      ]    in  5.0360 seconds.
VERBOSE: Operation 'Invoke CimMethod' complete.
VERBOSE: Time taken for configuration job to complete is 5.414 seconds

Let’s make a quick interpretation of the output.