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. Be sure to read the first parts of the series:
- Desired State Configuration: Part 1
- Desired State Configuration: Part 2
- Desired State Configuration: Part 3
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