How Do I Create a Desired State Configuration?

Over the last number of posts, we have taken a look at all the key concepts which we combine to implement our Desired State Configuration (DSC). We began with two different methods of implementing a pull server to host our DSC configurations: using a simple PowerShell Script and with a new Resource Provider for DSC published by Microsoft to implement the pull Server.

With our server online, we then changed the focus over to our nodes, where we reviewed the procedures needed to create the configurations necessary to set our nodes as pull clients to the server. We also spent a little time to consider what we may need to do, if the node had a requirement to be placed into “Maintenance Mode.” But all of this work is of no value if we do not understand what we can actually manage as part of a Desired Configuration, therefore we reviewed all the “Resource Providers” that Microsoft included in the solution, ready for us to consume.

Now we need to take this next step and combine these resources to define our Desired Configuration. We also need to publish this to the pull server, so that our configured nodes can retrieve their respective configurations and apply as defined in the configuration.

Sample Configuration

Building from our initial configuration sample, we can now start to consider how to leverage PowerShell to assist in creating a more dynamic DSC configuration for our servers. Previously we illustrated some samples that combined the resource providers into a simple configuration, however, we can make this more powerful if we modify our approach just a little.

Servers and Roles

Starting with a simple hash table, we can create an list of servers to which we will apply DSC settings. Using this table, we can then define the settings that might need to be applied to ensure their configurations are appropriate for the role that they are playing in our environment.

To illustrate this approach, let’s consider this simple table as an example.

​ $ConfigurationData = @{
   AllNodes = @(
      @{NodeName = 'PDC-SC-DC01';    ServerType='VM';       Roles='ADService'},
      @{NodeName = 'PDC-SC-VMM01';   ServerType='VM';       Roles='WebServer'},
      @{NodeName = 'PDC-FS-SMB01';   ServerType='VM';       Roles='FileServer'}
      @{NodeName = 'PDC-VM-HOST01';  ServerType='Physical'; Roles=@('VMHost','FileServer')}
   )
}

From this, we can quickly determine, that we have four servers, that three of these are virtual, and that the roles will be deployed on the respective servers. We could get all fancy and host this data in a configuration database, but this is going to be a great starting point.

Configuration Scripts

Now, that we have a table to work from, we can get to the real fun part, and start building our configuration using a combination of PowerShell and leveraging the resource providers we introduced earlier.

To make this easier, I will share a starting configuration, and we can review some of its elements. Rather than calling out each area, I will embed comments in the configuration, so be sure to read through carefully!

​ # We begin defining a configuration that will be common to all our server nodes
# This will include some basics standard settings, including SNMP Services, DotNet framework
# Disabling the annoying IE Enhanced Security stuff, and finally installing my SCOM Agent
# Note that the SCOMAgent is actually a configuration of its own; this allows us to embed
# configurations within each other, making it easier to read and create complex configurations

configuration BaseServer
{
   WindowsFeature snmp
   {
      Ensure = "Present"
      Name = 'SNMP-Service'
   }

   WindowsFeature snmpwmi
   {
      Ensure = "Present"
      Name = 'SNMP-WMI-Provider'
      DependsOn= '[WindowsFeature]snmp'
   }

   WindowsFeature DotNet {
      Ensure = "Present"
      Name = "NET-Framework-45-Core"
   }

   Registry IEEnhanchedSecurity
   {
      # When "Present" then "IE Enhanced Security" will be "Disabled"
      Ensure = "Present"
      Key = "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A7-37EF-4b3f-8CFC-4F3A74704073}"
      ValueName = "IsInstalled"
      ValueType = "DWord"
      ValueData = "0"
   }

   SCOMAgent InstallandRegister{}
}

# To leverge the power of DSC, we can define the configuration of our servers
# to be based any any combination of things, including physical and virtual.
# For virtual servers we could, for example, include integration components, while
# for physical servers we might want to deploy hardware management agents!

configuration VirtualServer
{
   BaseServer StandardBaseConfiguration {}
}

configuration PhysicalServer
{
   BaseServer StandardBaseConfiguration {}

   Dell DellManagementStuff
   {
      DependsOn  = '[BaseServer]JustTheBasics'
   }
}

# Of course to really leverage the power of DSC and PowerShell, our hash table is defined
# to describe the different roles that will be hosted on the node; therefore, we can then
# use that detail to define additional configuration instructions based on the roles

configuration UnconfiguredServer
{
   WindowsFeature PowerShellISE
   {
      Ensure = "Present"
      Name = 'PowerShell-ISE'
   }
}

configuration WebServer
{

   WindowsFeature IISServer
   {
      Ensure = "Present"
      Name = 'Web-WebServer'
   }
}

configuration FileServer
{
   WindowsFeature FileServices
   {
      Ensure = "Present"
      Name = 'FS-FileServer'
   }

   WindowsFeature FileResourceManagement
   {
      Ensure = "Present"
      Name = 'FS-Resource-Manager'
   }
}

configuration VMHost
{
   WindowsFeature Hyper-V
   {
      Ensure = "Present"
      Name = 'Hyper-V'
   }
}

# On our physical servers we choose to install the Dell Management tools
# But as these might be used on both Rack and Blade servers, we can break
# this into another configuration that can be referenced by other configurations!

configuration Dell
{
   Package DellOMSABinaries {
      Ensure    = "Present"
      Path      = "\\Server\Share\Dell\OpenManage\SysMgmtx64.msi"
      Name      = "Dell OpenManage Systems Management Software (64-Bit)"
      ProductID = "12345678-1234-12345678-12345678"
      Arguments = "ADDLOCAL=ALL"
   }
}

# Taking a similar approach, we can also manage our agents as different configurations,
# which can be easily rolled up into other configurations. In my case, this will be part
# of the base configuration for each server

Configuration SCOMAgent {

   file SCOMAgentInstaller
   {
      DestinationPath = 'c:\Installs\SCOM\Agent'
      SourcePath = "\\server\share\SCOM\Agent"
      Type = 'Directory'
      Recurse = 'False'
   }

   Package SCOMAgentPackage {
      Ensure = "Present"
      Name = "Microsoft Management Agent"
      Path = "C:\Installs\SCOM\Agent\MOMAgent.msi"
      Arguments = "USE_SETTINGS_FROM_AD=0 MANAGEMENT_GROUP=OM_Diginerve MANAGEMENT_SERVER_DNS=PDC-SC-OMMS01.diginerve.net ACTIONS_USE_COMPUTER_ACCOUNT=1 USE_MANUALLY_SPECIFIED_SETTINGS=1 SET_ACTIONS_ACCOUNT=1 AcceptEndUserLicenseAgreement=1"
      productId = "8B21425D-02F3-4B80-88CE-8F79B320D330"
      LogPath = "C:\Files\SCOMAgentInstallLog.txt"
      DependsOn = '[File]SCOMAgentInstaller'
   }
}

#
# The Heart of the Configuration! This is where all the work happens
# the Master Configuration accepts in the Configuration Data hash table to start running
# some very simple PowerShell logic to build out configurations for each server
# based on the details we hold in the hash table.
#
# Extending this configuration, you can create a profile ready for your enterprise.
# But don't forget to share it
# Create a copy at gist.github.com and put the links back in the comments!
#

configuration PetriDSC
{
   $DependsOn = $null

   if ($AllNodes.where({$_.ServerType -eq 'VM'}).NodeName)
   {
      node ($AllNodes.where({$_.ServerType -eq 'VM'})).NodeName
      {
         VirtualServer HyperV {}
         $DependsOn = '[VirtualServer]HyperV'
      }
   }

   if ($AllNodes.where({$_.ServerType -eq 'Physical'}).NodeName)
   {
      node ($AllNodes.where({$_.ServerType -eq 'Physical'})).NodeName
      {
         PhysicalServer Dell  {}
         $DependsOn = '[PhysicalServer]Dell'
      }
   }

   node ($AllNodes).NodeName
   {
      switch ($Node.Roles)
      {
         'WebServer'     { WebServer      WebServices    { DependsOn = $DependsOn } }
         'FileServer'    { FileServer     FileServices   { DependsOn = $DependsOn } }
         'VMHost'        { VMHost         VMServices     { DependsOn = $DependsOn } }
         default         { UnconfiguredServer NoRoleDefinedYet {} }
      }
   }
}

Now, we just need to register these configurations in our PowerShell environment, then we will use the master configuration, which I called PetriDCS, and pass in the hash table of our servers, which should be enough to have a set of configurations created, one per server.

Create a Desired State Configuration

The MOF files are ready. You can open them in Notepad. If everything worked to plan, each one should resemble the options you chose in your hash table.