Conceptualize Desired State Configuration: Part 4

Summary: Microsoft MVP, Will Anderson, adds features to his Desired State Configuration, updates the .mof file, and validates the configuration.

Hello, Ed Wilson here to welcome back this week’s guest blogger, MVP Will Anderson.

  Note   This is a seven-part series that includes the following posts:

The code used in this series can be downloaded from the Script Center Repository:
Conceptualize Desired State Configuration – Reference Script

To review…

We've taken a basic configuration script to create an SCCM distribution point, and using that as a reference, we've started to create our Desired State Configuration (DSC). In my last post, we verified that our syntax is good by successfully generating a .mof file, but we need to make sure that it actually works.

As I mentioned previously, I've added the remaining WindowsFeature configurations to save a little time. So now, the configuration should look like this:

configuration CMDPConfig

{

 Import-DscResource -ModuleName @{ModuleName = 'PSDesiredStateConfiguration'; ModuleVersion = '1.1' }

 Node ("LWINCM02")

 {

  # Call Resource Provider

  # E.g: WindowsFeature, File

  #Remove GUI Tools

  WindowsFeature RemoveUI

  {

   Ensure = "Absent"

   Name = "Server-Gui-Shell"

  }

  WindowsFeature EnableRemoteDifferentialCompression

  {

   Ensure = "Present"

   Name = "RDC"

   DependsOn = "[WindowsFeature]RemoveUI"

  }

  WindowsFeature IIS

  {

   Ensure = "Present"

   Name = "Web-Server"

   DependsOn = "[WindowsFeature]EnableRemoteDifferentialCompression"

  }

  WindowsFeature IIS6WMICompatibility

  {

   Ensure = "Present"

   Name = "Web-WMI"

   DependsOn = "[WindowsFeature]IIS"

  }

  WindowsFeature NetCore

  {

   Ensure = "Present"

   Name = "NET-Framework-Core"

   DependsOn = "[WindowsFeature]IIS6WMICompatibility"

  }

  WindowsFeature BITSCore

  {

   Ensure = "Present"

   Name = "BITS"

   DependsOn = "[WindowsFeature]NetCore"

  }

  WindowsFeature BITSIISExt

  {

   Ensure = "Present"

   Name = "BITS-IIS-Ext"

   DependsOn = "[WindowsFeature]BITSCore"

  }

  WindowsFeature BITSRSAT

  {

   Ensure = "Present"

   Name = "RSAT-Bits-Server"

   DependsOn = "[WindowsFeature]BITSIISExt"

  }

 }

}

CMDPConfig

Now we're going to push the configuration to our test system to verify that it works. I recommend you do this for each DSC resource that you add to your configuration (not each item you add). This way you're testing in blocks and verifying that you're configuring the resource providers correctly without throwing one giant hair-balled mess at the target all at once.

I treat DSC like I treat PowerShell…like LEGOs. I start with one piece, then add another, then another. We're doing the same here. Add a DSC resource, test it, push it, add another. Do it all over again until you have a finished product.

Setting the Local Configuration Manager

Before I once again generate a fresh .mof file, we're going to add one more thing to the mix, and that's to update the DSC Local Configuration Manager (LCM) settings. As you likely know, most Windows components that get installed require a reboot. So we want to make sure that when the configuration is finished, the LCM on the target machine will reboot the system. Before we begin, let's take a look at the default settings for the LCM on the target machine:

PS C:\Windows\system32> Get-DscLocalConfigurationManager -CimSession lwincm02

ActionAfterReboot    : ContinueConfiguration

AgentId      : FA621241-B2F2-11E5-80C0-000D3A331A74

AllowModuleOverWrite   : False

CertificateID     :

ConfigurationDownloadManagers : {}

ConfigurationID    :

ConfigurationMode    : ApplyAndMonitor

ConfigurationModeFrequencyMins : 15

Credential      :

DebugMode      : {NONE}

DownloadManagerCustomData  :

DownloadManagerName   :

LCMCompatibleVersions   : {1.0, 2.0}

LCMState      : Idle

LCMStateDetail     :

LCMVersion      : 2.0

StatusRetentionTimeInDays  : 10

PartialConfigurations   :

RebootNodeIfNeeded    : False

RefreshFrequencyMins   : 30

RefreshMode     : PUSH

ReportManagers     : {}

ResourceModuleManagers   : {}

PSComputerName     : lwincm02

PSComputerName     : lwincm02

By default, the LCM RebootNodeIfNeeded setting is configured to False, which means no reboot will take place on this system after the configuration is applied. We want to change this to True. We can do this directly from our configuration, with the Local Configuration Manager meta configuration provider. We'll put this as our first configuration step for logical purposes:

   LocalConfigurationManager

  {

   RebootNodeIfNeeded = $true

   ConfigurationMode = "ApplyAndAutoCorrect"

  }

When I rerun the configuration script, you'll notice that two files are generated: a .mof file and a .meta.mof file. This meta configuration file contains the configuration information for the LCM. (For further reading about how the LCM meta configuration file works and how you can configure it, see Understanding Meta Configuration in Windows PowerShell Desired State Configuration.)

One thing that you will notice is that unlike your DSC resource providers, you can't use the Get-DscResource cmdlet to pull a template, because it doesn't exist as a DSC resource.

Get-DscResource LocalConfigurationManager

CheckResourceFound : The term 'LocalConfigurationManager' is not recognized as the name of a Resource.

At C:\windows\system32\windowspowershell\v1.0\Modules\PSDesiredStateConfiguration\PSDesiredStateConfiguration.psm1:3844 char:13

+    CheckResourceFound $Name $Resources

+    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 + CategoryInfo   : NotSpecified: (:) [Write-Error], WriteErrorException

 + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,CheckResourceFound

However, it can still be embedded in the configuration script and generate the meta.mof:

/*

@TargetNode='LWINCM02'

@GeneratedBy=LWINAdmin

@GenerationDate=01/04/2016 15:18:49

@GenerationHost=LWINCM01

*/

instance of MSFT_DSCMetaConfiguration as $MSFT_DSCMetaConfiguration1ref

{

ConfigurationMode = "ApplyAndAutoCorrect";

 RebootNodeIfNeeded = True;

};

instance of OMI_ConfigurationDocument

{

 Version="2.0.0";

 MinimumCompatibleVersion = "1.0.0";

 CompatibleVersionAdditionalProperties= { };

 Author="LWINAdmin";

 GenerationDate="01/04/2016 15:18:49";

 GenerationHost="LWINCM01";

 Name="CMDPConfig";

};

Although we have the LCM configuration in the same script, we'll have to use a separate command to push the update to the LCM.

Pushing the configuration

Let's push our configuration to the target machine. We'll use Set-DscLocalConfigurationManager to run the meta configuration, and pipe the output to Start-DscConfiguration to kick the system configuration:

PS C:\Windows\system32> Set-DscLocalConfigurationManager -ComputerName lwincm02 -Path C:\Scripts\Configs\CMDPConfig -Verbose | Start-DscConfiguration -Verbose

VERBOSE: Performing the operation "Start-DscConfiguration: SendMetaConfigurationApply" on target "MSFT_DSCLocalConfigurationManager".

VERBOSE: Perform operation 'Invoke CimMethod' with following parameters, ''methodName' = SendMetaConfigurationApply,'className' = MSFT_DSCLocalConfigurationManager,'namespaceName' = root/Mic

rosoft/Windows/DesiredStateConfiguration'.

VERBOSE: An LCM method call arrived from computer LWINCM01 with user sid S-1-5-21-1571215351-4035735135-2881492658-1104.

VERBOSE: [LWINCM02]: LCM: [ Start Set  ]

VERBOSE: [LWINCM02]: LCM: [ Start Resource ] [MSFT_DSCMetaConfiguration]

VERBOSE: [LWINCM02]: LCM: [ Start Set  ] [MSFT_DSCMetaConfiguration]

VERBOSE: [LWINCM02]: LCM: [ End Set  ] [MSFT_DSCMetaConfiguration] in 0.0290 seconds.

VERBOSE: [LWINCM02]: LCM: [ End Resource ] [MSFT_DSCMetaConfiguration]

VERBOSE: [LWINCM02]: LCM: [ End Set  ]

VERBOSE: [LWINCM02]: LCM: [ End Set  ] in 0.1890 seconds.

VERBOSE: Operation 'Invoke CimMethod' complete.

VERBOSE: Set-DscLocalConfigurationManager finished in 0.477 seconds.

VERBOSE: Time taken for configuration job to complete is 0.486 seconds

If we want to verify that our configuration is taking place, we'll verify that the target is processing the configuration:

PS C:\Windows\system32> Get-DscLocalConfigurationManager -CimSession lwincm02

ActionAfterReboot    : ContinueConfiguration

AgentId      : FA621241-B2F2-11E5-80C0-000D3A331A74

AllowModuleOverWrite   : False

CertificateID     :

ConfigurationDownloadManagers : {}

ConfigurationID    :

ConfigurationMode    : ApplyAndAutoCorrect

ConfigurationModeFrequencyMins : 15

Credential      :

DebugMode      : {NONE}

DownloadManagerCustomData  :

DownloadManagerName   :

LCMCompatibleVersions   : {1.0, 2.0}

LCMState      : Idle

LCMStateDetail     :

LCMVersion      : 2.0

StatusRetentionTimeInDays  : 10

PartialConfigurations   :

RebootNodeIfNeeded    : True

RefreshFrequencyMins   : 30

RefreshMode     : PUSH

ReportManagers     : {}

ResourceModuleManagers   : {}

PSComputerName     : lwincm02

PSComputerName     : lwincm02

We can see that the LCM RebootNodeIfNeeded meta configuration is set to True, and that the system is processing a new configuration. If we want to take a look at where the system is in the configuration, we can check the DSC Operational Event Logs. Note that I don't yet have all of the firewall rules in place to allow RPC calls between these systems. I'll be handling that in the next bit. But for now, I'll just wrap this up by using Invoke-Command:

PS C:\Windows\system32> Invoke-Command -ComputerName lwincm02 -ScriptBlock { Get-WinEvent -LogName "Microsoft-Windows-Dsc/Operational" | Where-Object LevelDisplayName -EQ 'Information'| Select-Object -First 1 | Format-List}

TimeCreated : 1/7/2016 3:57:56 PM

ProviderName : Microsoft-Windows-DSC

Id   : 4251

Message  : Job {6A20C51E-B557-11E5-80C1-000D3A331A74} :

    Operation Get-DscLocalConfigurationManager completed successfully.

I've done some filtering so I can see my last information message. Unfortunately, before I could run another command to expand the Message property, the system rebooted. But that also means that things are working! So we'll take a look after the reboot and see how the system looks.

What's the first thing we look for? Well, we told the server to remove its UI, so let’s see what happened…

Success!

Verifying the configuration

We could go through each individual setting manually and verify the configuration, or we could do it the DSC way with Test-DscConfiguration. There are a few ways to do this, and I'll quickly go through them.

PS C:\Windows\system32> Test-DscConfiguration -ComputerName lwincm02

True

Running Test-DscConfiguration against the target returns a simple True or False statement. This is OK if you only want to make sure that your configuration is fully compliant, but we get no data from it. So let's use the -Detailed parameter:

PS C:\Windows\system32> Test-DscConfiguration -ComputerName lwincm02 -Detailed

PSComputerName ResourcesInDesiredState  ResourcesNotInDesiredState  InDesiredState

————– ———————–  ————————–  ————–

lwincm02  {[DiskResize]CDrive, [DiskP…        True

That gives me a little more information, but I really want to see all of my configuration items with their status, so I’ll use this script:

PS C:\Windows\system32> Test-DscConfiguration -ComputerName lwincm02 -Detailed | Select-Object -ExpandProperty ResourcesInDesiredState

ConfigurationName : CMDPConfig

DependsOn   :

ModuleName   : DiskSize

ModuleVersion  : 1.0.0.0

PsDscRunAsCredential :

ResourceId   : [DiskResize]CDrive

SourceInfo   : C:\Scripts\Configs\CMDPConfig.ps1::22::9::DiskResize

DurationInSeconds : 0.073

Error    :

FinalState   :

InDesiredState  : True

InitialState   :

InstanceName   : CDrive

RebootRequested  : False

ResourceName   : DiskResize

StartDate   : 1/7/2016 5:54:27 PM

PSComputerName  : lwincm02

ConfigurationName : CMDPConfig

DependsOn   :

ModuleName   : DiskSize

ModuleVersion  : 1.0.0.0

PsDscRunAsCredential :

ResourceId   : [DiskPartition]EDrive

SourceInfo   : C:\Scripts\Configs\CMDPConfig.ps1::29::9::DiskPartition

DurationInSeconds : 0.139

Error    :

FinalState   :

InDesiredState  : True

InitialState   :

InstanceName   : EDrive

RebootRequested  : False

ResourceName   : DiskPartition

StartDate   : 1/7/2016 5:54:27 PM

PSComputerName  : lwincm02

ConfigurationName : CMDPConfig

DependsOn   :

ModuleName   : PSDesiredStateConfiguration

ModuleVersion  : 1.1

PsDscRunAsCredential :

ResourceId   : [WindowsFeature]RemoveUI

SourceInfo   : C:\Scripts\Configs\CMDPConfig.ps1::36::9::WindowsFeature

DurationInSeconds : 1.387

Error    :

FinalState   :

InDesiredState  : True

InitialState   :

InstanceName   : RemoveUI

RebootRequested  : False

ResourceName   : WindowsFeature

StartDate   : 1/7/2016 5:54:27 PM

PSComputerName  : lwincm02

ConfigurationName : CMDPConfig

DependsOn   : {[WindowsFeature]RemoveUI}

ModuleName   : PSDesiredStateConfiguration

ModuleVersion  : 1.1

PsDscRunAsCredential :

ResourceId   : [WindowsFeature]EnableRemoteDifferentialCompression

SourceInfo   : C:\Scripts\Configs\CMDPConfig.ps1::42::9::WindowsFeature

DurationInSeconds : 2.592

Error    :

FinalState   :

InDesiredState  : True

InitialState   :

InstanceName   : EnableRemoteDifferentialCompression

RebootRequested  : False

ResourceName   : WindowsFeature

StartDate   : 1/7/2016 5:54:27 PM

PSComputerName  : lwincm02

ConfigurationName : CMDPConfig

DependsOn   : {[WindowsFeature]EnableRemoteDifferentialCompression}

ModuleName   : PSDesiredStateConfiguration

ModuleVersion  : 1.1

PsDscRunAsCredential :

ResourceId   : [WindowsFeature]IIS

SourceInfo   : C:\Scripts\Configs\CMDPConfig.ps1::50::9::WindowsFeature

DurationInSeconds : 3.471

Error    :

FinalState   :

InDesiredState  : True

InitialState   :

InstanceName   : IIS

RebootRequested  : False

ResourceName   : WindowsFeature

StartDate   : 1/7/2016 5:54:27 PM

PSComputerName  : lwincm02

ConfigurationName : CMDPConfig

DependsOn   : {[WindowsFeature]IIS}

ModuleName   : PSDesiredStateConfiguration

ModuleVersion  : 1.1

PsDscRunAsCredential :

ResourceId   : [WindowsFeature]IIS6WMICompatibility

SourceInfo   : C:\Scripts\Configs\CMDPConfig.ps1::57::9::WindowsFeature

DurationInSeconds : 4.918

Error    :

FinalState   :

InDesiredState  : True

InitialState   :

InstanceName   : IIS6WMICompatibility

RebootRequested  : False

ResourceName   : WindowsFeature

StartDate   : 1/7/2016 5:54:27 PM

PSComputerName  : lwincm02

ConfigurationName : CMDPConfig

DependsOn   : {[WindowsFeature]IIS6WMICompatibility}

ModuleName   : PSDesiredStateConfiguration

ModuleVersion  : 1.1

PsDscRunAsCredential :

ResourceId   : [WindowsFeature]NetCore

SourceInfo   : C:\Scripts\Configs\CMDPConfig.ps1::65::9::WindowsFeature

DurationInSeconds : 6.542

Error    :

FinalState   :

InDesiredState  : True

InitialState   :

InstanceName   : NetCore

RebootRequested  : False

ResourceName   : WindowsFeature

StartDate   : 1/7/2016 5:54:27 PM

PSComputerName  : lwincm02

ConfigurationName : CMDPConfig

DependsOn   : {[WindowsFeature]NetCore}

ModuleName   : PSDesiredStateConfiguration

ModuleVersion  : 1.1

PsDscRunAsCredential :

ResourceId   : [WindowsFeature]BITSCore

SourceInfo   : C:\Scripts\Configs\CMDPConfig.ps1::73::9::WindowsFeature

DurationInSeconds : 7.357

Error    :

FinalState   :

InDesiredState  : True

InitialState   :

InstanceName   : BITSCore

RebootRequested  : False

ResourceName   : WindowsFeature

StartDate   : 1/7/2016 5:54:27 PM

PSComputerName  : lwincm02

ConfigurationName : CMDPConfig

DependsOn   : {[WindowsFeature]BITSCore}

ModuleName   : PSDesiredStateConfiguration

ModuleVersion  : 1.1

PsDscRunAsCredential :

ResourceId   : [WindowsFeature]BITSIISExt

SourceInfo   : C:\Scripts\Configs\CMDPConfig.ps1::81::9::WindowsFeature

DurationInSeconds : 8.498

Error    :

FinalState   :

InDesiredState  : True

InitialState   :

InstanceName   : BITSIISExt

RebootRequested  : False

ResourceName   : WindowsFeature

StartDate   : 1/7/2016 5:54:27 PM

PSComputerName  : lwincm02

What I like about this format is that each configuration item gives you the name of the configuration that applied the setting, the module it's using, and dependencies if you've configured them. There's a wealth of other information in here!

We can also go through the DSC Operational event logs and mine for the data that we want, and maybe set up alerts if the system drifts from its configuration. But for now, we have validation that the system is correctly configured.

Now that we've used one of the out-of-the-box DSC resources and done some basic configurations, we're going to move on to finding and importing additional DSC resources. We'll also cover using the Script DSC resource, and weigh the pros and cons of using this resource over creating your own DSC resources.

‘Til next time!

~Will

Thanks, Will, for another great installment of this series. Will will be back tomorrow with Part 5.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. Also check out my Microsoft Operations Management Suite Blog. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy