Author Archives: Steven Murawski

Going Deeper on DSC Resources


Desired State Configuration is a very new technology and declarative configuration management is a very young space yet.  We (Microsoft and the community) are still figuring out the best structure for resources, composite configurations, and other structures.

That said, there are certain viewpoints that I’ve come to, either from hands on experience or in watching how other communities (like the Puppet community or Chef community) handle similar problems.

How Granular Should I Get?

There is no absolute answer.

Very, Very Granular

Resources should be very granular in the abstract, but in practice, you may need to make concessions to improve the user experience.

For example, when I configure an IP address for a network interface, I can supply a default gateway. A default gateway is a route, which is separate from the interface and IP address, but in practice they tend to be configured together. In this case, it might make sense to offer a resource that can configure both the IP address and the default gateway.

I tend to think resources should be very granular. We can use composite resources to offer higher level views of the configuration. If I were implementing a resource to configure a network adapter’s IP and gateway, I would have a route resource, an IP address resource, and probably a DNS server setting resource. I would then also have a composite resource to deal with the default use case of configuring a network adapter’s IP address, gateway, and DNS servers together.

The benefit of doing it this way is that I still have very discrete, flexible primitives (the IP address resource, the route resource, and the DNS server resource). I can then leverage the route resource to create static routes, or use them directly to more discretely configure the individual elements.

Unless…

You have some flow control that you need to happen based on the state of the client or the environment.  Since your configuration is statically generated and is declarative, there are no flow control statements in the configuration MOF document.  That means that any logic that needs to occur at application time

Unfortunately, this leads to the need to re-implement common functionality.  For example, if I have a service that I need to be able to update the binary (not via an MSI), I need to basically re-implement parts of the file and service resource.  This use case requires a custom resource because I need to stop the service before I can replace the binary, but I don’t want to stop the service with every consistency check if I don’t need to replace the file.

This scenario begs for a better way to leverage existing resources in a cross resource scenario (kind of like RequiredModules in module metadata), but there isn’t a clean way to do this that I’ve found (but I’m still looking!).

My Recommendation

So for most cases, I would try to use existing resources or build very granular custom resources.  If I need to offer a higher level of abstraction, I’d escalate to putting a composite resource on top of those granular resources.  Finally, if I need some flow control or logic for a multistep process, I’d implement a more comprehensive resource.

What Should I Validate?

Now that we are seeing some more resources in the community repository (especially thanks to the waves of resources from the Powershell Team!), we are seeing a variety of levels of validation being performed.

I think that the Test-TargetResource function should validate all the values and states that Set-TargetResource can set.

An example of where this isn’t happening currently is in the cNetworking resource for PSHOrg_cIPAddress.  I’m going to pick on this resource a bit, since it was the catalyst for this discussion.

The resource offers a way to set a default gateway as well as the IP address.  So what happens if after setting the IP and default gateway, someone changes the default gateway to point to another router?

In this case, the validation is only checking that the IP address is correct.  DSC will never re-correct the gateway and our DSC configuration document (the MOF file) is no longer an accurate representation of the system state, despite the fact that the Local Configuration Manager (LCM) will report that everything matches.

This is BAD!!  If a resource offers an option to configure a setting, that setting should be validated by Test-TargetResource, otherwise that setting should be removed from the resource.  The intent of DSC is to control configuration, including changes over time and return a system to the desired state.  If we ignore certain settings, we weaken our trust in the underlying infrastructure of DSC.

What should I return?

The last element I’m going to tackle today is what should be returned from Get-TargetResource.  I’ve been on the fence about this one.  Like with Test-TargetResource, there are a number of implementation examples that vary in how they come up with the return values.

Currently, I don’t see a ton of use for Get-TargetResource and it doesn’t impact the Test and Set phases of the LCM, so it’s been easy to ignore.  This is bad practice (shame on me).

Here’s my thoughts around Get-TargetResource.  It should return the currently configured state of the machine.  Directly returning parameters passed in is misleading.

Going back to the PSHOrg_cIPAddress from the earlier example, it directly returns the default gateway from the parameter, regardless of the configured gateway.  This wouldn’t be so bad if the resource actually checked the gateway during processing and could correct it if it drifted.  But it does not check the gateway, so Get-TargetResource could be lying to you.  T

he most consistent result of Get-TargetResource would be retrieving the currently configured settings.

What’s left?

What other burning questions do you have around DSC?  Let’s keep talking them through either in the forums or in the comments here.

Building Desired State Configuration Custom Resources


Now that we’ve suitably rested, let’s get back to working with Desired State Configuration.  Now, there are some basic features to work with that ship by default and the PowerShell team has been blogging some additional resources, but in order to do some really interesting thing with DSC, we’ll need to create our own resources.

The High Points

The DSC Resource Structure

DSC resources are (at their most basic) a PowerShell module.  These modules are augmented by a schema.mof file (we’ll get into that more in a minute or two).  These modules expose three main functions, Get-TargetResource, Set-TargetResource, and Test-TargetResource.  All three functions should share the same set of parameters.

Test-TargetResource

Test-TargetResource validates whether your resource is currently in the desired state based on the parameters provided.  This function returns a boolean, $true if the resource is in the state described or $false if not.

Set-TargetResource

Set-TargetResource is the workhorse in this module.  This is what will get things into the correct state.  The convention is to support one parameter called Ensure that can take two values, “Present” or “Absent” to describe whether or not a resource should be applied or removed as described.

(Here’s a little trick.. if you write break your Test-TargetResource into discrete functions, you can use those functions to only run the portions of Set-TargetResource that you need to!)

Get-TargetResource

This is currently the least useful of the commands, but if experience has taught me anything, it’ll likely have an a growing use case over time.

Get-TargetResource returns the current state of the of the resource, returning a hash table of properties matching the parameters supplied to the command.

Exporting Commands

This module should explicitly export these commands via either Export-ModuleMember or a module manifest.  If you don’t, Import-DscResource will have trouble loading the resources when you try to generate a configuration (it’s not a problem for running a configuration, just the generation part).

The Managed Object Framework (MOF) Schema

The last piece of the DSC Resource is a schema file that maps the parameters for the command to a CIM class that can be registered in WMI.  This allows us to serialize the configuration parameters to a standards-based format and allows the Local Configuration Manager to marshal the parameters back to call the PowerShell functions for the phase that the LCM is in.  This file is named modulename.schema.mof.

There is no real reason to write a schema.mof file by hand, both the DSC Resource Designer and my New-MofFile function can help generate that function.  The one key thing to be aware of in the schema.mof is that there is an attribute at the top of each of the MOF classes that denotes a friendly name, which is the identifier you will use in a configuration to specify a resource.

[ClassVersion("1.0.0"), FriendlyName("Pagefile")]

How To Structure a Module With Resources

To get a good idea of the resource structure, we can look at the StackExchangeResources module in the PowerShell.Org GitHub repository.  There is a base module – StackExchangeResources, which has a module metadata file (required, you’ll see why in a minute).  In that module, we need a folder DSCResources.  Our custom resource will be placed under that folder.

The reason we need a module metadata file for the base module, is when resources from that module are used in a configuration, the generated configuration MOF files will reference the version of the base module (and that specific version is required on the node where the resource will be applied).

Next up, we’ll talk about how we package our resources to be distributed by a pull server.

Configuring a Desired State Configuration Client


Once we have our pull server in place and we’re starting to create configurations, we need to set up our client nodes to be able to connect to the pull server and how we want the node to behave.

The High Points

Examining the Local Configuration Manager

The Desired State Configuration agent included in Windows Management Framework 4 (or natively on Server 2012 R2 / Windows 8.1) is exposed through the Local Configuration Manager.

PS> Get-DscLocalConfigurationManager

AllowModuleOverwrite           : False
CertificateID                  :
ConfigurationID                :
ConfigurationMode              : ApplyAndMonitor
ConfigurationModeFrequencyMins : 30
Credential                     :
DownloadManagerCustomData      :
DownloadManagerName            :
RebootNodeIfNeeded             : False
RefreshFrequencyMins           : 15
RefreshMode                    : PUSH
PSComputerName                 :

This is where we can configure the behavior of DSC for a particular node.  So, how do we configure it?  With DSC of course!

There is a configuration option LocalConfigurationManager that allows us to set values for the Local Configuration Manager.  A sample configuration looks something like this:

configuration LetsGetConfiguring
{
    param ($NodeId, $PullServer)    

    LocalConfigurationManager
    {
        AllowModuleOverwrite = 'True'
        ConfigurationID = $NodeId
        ConfigurationModeFrequencyMins = 60 
        ConfigurationMode = 'ApplyAndAutoCorrect'
        RebootNodeIfNeeded = 'True'
        RefreshMode = 'PULL' 
        DownloadManagerName = 'WebDownloadManager'
        DownloadManagerCustomData = (@{ServerUrl = "https://$PullServer/psdscpullserver.svc"})
    }
}

While this configuration looks similar to other configurations we might create, we need to apply it with a different command – Set-DscLocalConfigurationManager.

LetsGetConfiguring -NodeId 71defb7f-232b-4213-b289-08c3d424e162 -PullServer pullserver.somedomain.com
Set-DscLocalConfigurationManager -path LetsGetConfiguring

The Local Configuration Manager offers a number of options, which we’ll examine.

AllowModuleOverwrite

This one is pretty straight-forward and only impacts configurations where you are using a pull server.  If you allow module overwrite, newer versions of modules can replace existing modules.  If you don’t enable this, you’ll have to manually remove modules if you want a new copy to pull down.

CertificateID

CertficateID is a thumbprint of a certificate in the machine certificate store that will be used to decrypt any secrets present in the configuration.  DSC allows PSCredential objects to be marshaled through a MOF file, but requires them (without explicit authorization) to be encrypted. (There is another option as well, if you use the ConfigurationData feature, you can also supply the path to a certificate file to use – I’ll be blogging that scenario later when I cover some more advanced scenarios.)

ConfigurationID

The ConfigurationID is a GUID which uniquely identifies what configuration a node should retrieve from a pull server.  If you haven’t had to generate GUIDs before, a really easy way to do so is:

PS> [guid]::NewGuid().Guid

ConfigurationMode

ConfigurationMode defines how the DSC client operates.  There are three valid values:

  • Apply
  • ApplyAndMonitor
  • ApplyAndAutoCorrect

(NOTE:  These descriptions of functionality are based on limited testing – the TechNet documentation is not up to date yet, but should be in the near future.)

Apply will apply the configuration once and after a successful run is logged, it will stop attempting to apply configuration or checking the configuration.  ApplyAndMonitor will apply a configuration as in Apply, but will continue to validate that a node is configured as described.  No corrective action will take place if there is configuration drift.  Finally, ApplyAndAutoCorrect is what most of us think of when looking at DSC as a configuration management tool.  This setting applies a configuration and checks it regularly.  If configuration drift is detected, the configuration manager will attempt to return the machine to the desired state (see how I worked the product name in there..).

ConfigurationModeFrequencyMins

This setting determines how frequently the configured method (the RefreshMode) will be run.  In the case of a pull server, this is how frequently the pull server will be checked for updated configurations.  The minimum value for this is 30.  This value needs to be a multiple of the RefreshFrequencyMins.  If it is not, the engine will treat it as if it was a multiple (rounded up).

Credential

The Credential supplied can be used for accessing remote resources.

DownloadManagerCustomData

DownloadManagerCustomData is a hashtable of values that is passed to the specified download manager.  In the case of a a pull server, the two possible keys are ServerUrl and AllowUnsecureConnection.

DownloadManagerName

Here is where we specify which download manager to use.  DSC ships with two options, the WebDownloadManager (for the web-based pull server) and the DSCFileDownloadManager (for using an SMB share).

RebootNodeIfNeeded

Here’s another pretty self-explanatory setting.  DSC offers a method for resources to request a reboot.  If this setting is $true, then DSC will reboot the node when it is requested.  If it is set to $false, DSC will notify (via the verbose stream and the DSC log) that a reboot is required, but not actually reboot the node.

RefreshFrequencyMins

The RefreshFrequencyMins setting determines how often DSC runs an integrity check against the cached configuration value (or if the check falls on the ConfigurationModeFrequencyMins interval against the pull server if one is configured).  The minimum value for this setting is 15 minutes.

RefreshMode

RefreshMode is either PUSH or PULL.  If you set the RefreshMode to PULL, you’ll need to configure a download manager (via DownloadManagerName).

Next up, we’ll look at how we can build custom resources.

Desired State Configuration – General Availability Changes


PowerShell DSC, along with Windows Server 2012 R2 has reached General Availability!  Yay!

However, there is (at least one so far) breaking change in Desired State Configuration (DSC).

Fortunately, the change is in an area I haven’t blogged about yet.. creating custom resources.  Unfortunately, it does mean I’ll have to update the GitHub repository and all my internal content (should be done by early next week).

The short version is that DSC resources are now resources inside modules, rather than each resource being independent modules.  The benefit of this is that now DSC resources won’t pollute the module scope, each resource won’t need its own psd1 file (the source module will require one though), and it provides an easier way to group resources, which wasn’t really possible before.

So, with GA, resources should go under the module root in a folder DSCResources.  You can have one or more resources in one PowerShell module.  The PowerShell module version is what will be used for the resource version number, so if you have several resources, a version number bump affects all the resources in the module.

I’ll be picking back up with the DSC series next week with how to configure DSC clients, so stay tuned.

Building a Desired State Configuration Configuration – Part 2


Ok, let’s get back to creating a DSC configuration.  If you haven’t read the last post in this series, go back and do that now, I’ll wait.  Now with that out of the way, let’s get back to it…

The High Points

Picking Back UP

Now that we have some of the basics down, we can start to look deeper at how composable these configurations are. A DSC configuration defined in PowerShell offers several advantages, not the least of which is that a configuration can be parameterized.

Parameterization

configuration MyFirstServerConfig
{
	param ([string[]]$NodeName)
	node $NodeName
	{
		WindowsFeature snmp
        {
            Name = 'SNMP-Service'
        }
	}
}

With this simple tweak, I’ve taken a configuration that was hard-coded to one server name to one that can take an array of server names. The PowerShell savvy are probably going, “Big deal.. functions could do that since Monad”. If you remember back in the last post, I showed how ConfigurationData could be used to pass data into a configuration. Then my main configuration did some stuff based on metadata about the node. My configuration was starting to look a bit complicated. The ability to parameterize configurations really helps us when we are ready for the next step, nesting configurations.

Nesting Configurations

Let’s start with an example…

$ConfigurationData = @{
  AllNodes = @(
    @{NodeName = 'Server1';Role='Web'},
    @{NodeName = 'Server2';Role='FileShare'}
    @{NodeName = 'Server3';Role=@('FileShare','Web')}
  )
}

configuration RoleConfiguration
{
	param ($Roles)

	switch ($Roles)
    {
        'FileShare' {
                        WindowsFeature FileSharing
                        {
                            Name = 'FS-FileServer'
                        } 
                    }
        'Web'       {
                        WindowsFeature Web
                        {
                            Name = 'web-Server'
                        } 
                    }        
    }
}

configuration MyFirstServerConfig 
{
    node $allnodes.NodeName
    {
        WindowsFeature snmp
        {
            Name = 'SNMP-Service'
        }       

        RoleConfiguration MyServerRoles
        {
        	Roles = $Node.Role
    	}
    }
}

So, what did we just see? I defined a parameterized configuration and then used it like a DSC Resource in my main configuration. Parameters are passed to the nested configuration in the exact same way as to a DSC Resource. This syntax also means that we can use DependsOn to create dependency chains between groups of functionality more easily.

configuration MyFirstServerConfig 
{
    node $allnodes.NodeName
    {
        WindowsFeature snmp
        {
            Name = 'SNMP-Service'
        }       

        RoleConfiguration MyServerRoles
        {
        	Roles = $Node.Role
        	DependsOn = '[WindowsFeature]snmp'
    	}
    }
}

We can leverage this technique of creating nested configurations to simplify our configuration scripts, minimize dependency chains, and provide an easy way to reuse configuration sections for multiple configurations, all using the same semantics of any DSC resource.

Applying Configurations

Once we have our configurations generated, we have a couple of ways to distribute and apply the configurations. We’ll start assuming that we have generated our configurations for the servers we would like to target.

Start-DscConfiguration

Our first option is Start-DscConfiguration. We can point Start-DscConfiguration to the configuration files that we’ve generated (just point to the directory with the configuration files in them).

Start-DscConfiguration -Path ./MyFirstServerConfig

Doing this will attempt to run the configurations generated against any nodes specified. You can target specific servers by using the -computername or -cimsession parameters.

One downside to using Start-DscConfiguration is that any custom resources (not nested configurations) need to be present on the remote node BEFORE applying the configuration.

You CANNOT create a configuration that uses the file resource (or any other resource) to create the resource on disk during the DSC run. While this would be a cool trick, the resources contain a schema.mof file that defines the interface that DSC can use and the DSC engine will error if it cannot find the resource interface when the configuration is validated before it applies. One option is having two-phased configurations, one to distribute resources and the second to apply it.

Pulling a Configuration

The next alternative is to distribute configurations and resources using a pull Server. In box, DSC supports two types of pull server, an REST based pull server (like described in my previous post) and an SMB based pull server (described here). The pull server requires nodes to be labeled with a GUID (the configuration ID, which we’ll talk about in an upcoming post), instead of server name. The pull server also requires that each config be accompanied by a checksum file with the file hash of the configuration file (example 72ed4117-fc49-4f81-822c-5bc59db64dd3.mof and 72ed4117-fc49-4f81-822c-5bc59db64dd3.mof.checksum).  One word off caution.. there can be no extra whitespace after the hash in the checksum file or the hash check will fail on the client node.  This means you cannot use

Get-FileHash 72ed4117-fc49-4f81-822c-5bc59db64dd3.mof | out-file 72ed4117-fc49-4f81-822c-5bc59db64dd3.mof.checksum

or

Get-FileHash 72ed4117-fc49-4f81-822c-5bc59db64dd3.mof | set-content 72ed4117-fc49-4f81-822c-5bc59db64dd3.mof.checksum

as those leave extra whitespace at the end of the file. I’ve been using

[System.IO.File]::AppendAllText('72ed4117-fc49-4f81-822c-5bc59db64dd3.mof.checksum', (Get-FileHash 72ed4117-fc49-4f81-822c-5bc59db64dd3.mof).Hash)

In my next post, I’ll be talking about we can configure our clients to talk to a pull server, then we can see stuff really start to happen.

Building a Desired State Configuration Configuration


Now that’s a title!  We’ve worked through my reasoning as to why I want Desired State Configuration (DSC) and how to build a pull server.  Today and in the next post we are going to look at how to create configurations which describe how our target systems are supposed to work.

The High Points

Building Configurations

Configurations are the driving force for DSC.  A configuration is a Managed Object Format (MOF) document that describes the how a specified server (or servers) should look.

What You See

A basic configuration may look like

/*
@TargetNode='8c7bfb10-8540-4a89-904c-5e6759de6d80'
@GeneratedBy=svc_build
@GenerationDate=10/07/2013 19:43:24
@GenerationHost=OR-WEB01
*/

instance of Pagefile as $Pagefile1ref
{
ResourceID = "[Pagefile]Default::[BaseServer]JustTheBasics::[VirtualServer]VMWare";
 InitialSize = 4294967296;
 SourceInfo = "C:\\windows\\system32\\WindowsPowerShell\\v1.0\\Modules\\SELocalConfiguration\\StackExchangeConfiguration\\StackExchangeConfiguration.psm1::14::5::Pagefile";
 ModuleName = "Pagefile";
 MaximumSize = 4294967296;
 ModuleVersion = "1.0";
};

instance of PowerPlan as $PowerPlan1ref
{
ResourceID = "[PowerPlan]Default::[BaseServer]JustTheBasics::[VirtualServer]VMWare";
 SourceInfo = "C:\\windows\\system32\\WindowsPowerShell\\v1.0\\Modules\\SELocalConfiguration\\StackExchangeConfiguration\\StackExchangeConfiguration.psm1::20::5::PowerPlan";
 Name = "High performance";
 ModuleName = "PowerPlan";
 ModuleVersion = "1.0";
};

instance of MSFT_RoleResource as $MSFT_RoleResource1ref
{
ResourceID = "[WindowsFeature]snmp::[BaseServer]JustTheBasics::[VirtualServer]VMWare";
 SourceInfo = "C:\\windows\\system32\\WindowsPowerShell\\v1.0\\Modules\\SELocalConfiguration\\StackExchangeConfiguration\\StackExchangeConfiguration.psm1::25::5::WindowsFeature";
 Name = "SNMP-Service";
 ModuleName = "MSFT_RoleResource";
 ModuleVersion = "1.0";
};

instance of OMI_ConfigurationDocument
{
 Version="1.0.0";
 Author="build_service";
 GenerationDate="10/07/2013 19:43:24";
 GenerationHost="OR-WEB01";
};

Each instance of a MOF class (except for the OMI_ConfigurationDocument) refer to a DSC Resource and provides the parameters that resource will be called with when the configuration engine runs.  There are a couple of properties that are not passed to the resource module.  The ResourceID is a unique identifier that indicates the resource and the configuration inheritance tree where it is defined (we’ll dig deeper into that shortly).  The ModuleVersion is the version number of the PowerShell module (from the psd1) of the DSC Resource.

Getting From Here To There

We don’t want to write straight MOF files to define configuration, mainly because they are kind of verbose, with a some boilerplate  stuff for each resource.  Fortunately, we’ve got a Domain Specific Language (DSL) in PowerShell v4 to generate them.

The Configuration Keyword

PowerShell v4 contains the keyword “configuration”, which allows us to provide a name for the configuration (like a function name).

configuration MyFirstServerConfig 
{
}

It looks just like how you would define a function or workflow. Now let’s put something useful inside of it.

configuration MyFirstServerConfig 
{
    WindowsFeature snmp
    {
        Name = 'SNMP-Service'
    }
}

In this most simple of examples, we’ve defined a particular feature to be installed on a Windows Server. When we run this snippet, a wrapper function will be generated (kind of like how a workflow wrapper is generated). At this point, no MOF file has been created or applied, this simply creates a function that can generate a configuration based on the resources specified within. If we execute this configuration

PS> MyFirstServerConfig

we’ll get a file named localhost.mof in a folder at $pwd/MyFirstServerConfig.

Configuration Default Parameters – OutputPath

If we want to specify the server the configuration applies to, we can wrap the resources in a Node block.

configuration MyFirstServerConfig 
{
    Node Server1
    {
      WindowsFeature snmp
      {
          Name = 'SNMP-Service'
      }
    }
}

This will create a configuration named Server1. Node names will be important as we move on to talking about targeting via Start-DscConfiguration and using the pull server.

We do have some options as to how the configuration gets generated. We can use the OutputPath to control where the configuration files are deposited.

PS> MyFirstServerConfig -OutputPath c:\Configurations
Configuration Default Parameters – ConfigurationData

Our other major parameter is ConfigurationData. ConfigurationData is a way to separate out your environmental concerns from the configuration documents. We’ll come back to this one after we explore a few more concepts. ConfigurationData is a hashtable that expects a certain structure. The hashtable should contain an key named AllNodes, which is an array of hashtables that describe the nodes whose data you want to inject. For example

$ConfigurationData = @{
  AllNodes = @(
    @{NodeName = 'Server1';Role='Web'},
    @{NodeName = 'Server2';Role='FileShare'}
  )
}

NodeName is a common convention for specifying the node name.  We don’t want to use Node, as there are some automatic variables populated in a configuration, one of which is $Node.  All the other keys in the hashtable representing a node are completely up to you.

Just a quick aside.. the node name does not necessarily equate to the server name.  When we get in to targeting (a bit in this post and more in an upcoming one), we’ll see how this is true.

After we have some data in our ConfigurationData hashtable (and the variable doesn’t need to be called ConfigurationData, I just did for convenience sake), we can use that to help drive our configuration. We’ll tweak our configuration function a bit, so that it can take advantage of the extra data being supplied.

configuration MyFirstServerConfig 
{
    node $allnodes.NodeName
    {
        WindowsFeature snmp
        {
            Name = 'SNMP-Service'
        }       
        switch ($Node.Role)
        {
            'FileShare' {
                            WindowsFeature FileSharing
                            {
                                Name = 'FS-FileServer'
                            } 
                        }
            'Web'       {
                            WindowsFeature Web
                            {
                                Name = 'web-Server'
                            } 
                        }
        }
    }
}

Since this is a PowerShell DSL, I can use PowerShell functions, operators, and flow control to manipulate the configuration details. In this case, I’m using a switch statement to add roles to my server based on role definitions I’m supplying in my ConfigurationData.

PS> MyFirstServerConfig -ConfigurationData $ConfigurationData

    Directory: C:\scripts\MyFirstServerConfig

Mode                LastWriteTime     Length Name                                                                              
----                -------------     ------ ----                                                                              
-a---         10/8/2013   4:03 PM       1494 Server1.mof                                                                       
-a---         10/8/2013   4:03 PM       1516 Server2.mof

If we look at the MOF files generated by this, we’ll see that Server1 does not have the FS-FileServer role, but does have the Web-Server role.

/*
@TargetNode='Server1'
@GeneratedBy=smurawski
@GenerationDate=10/08/2013 16:03:51
@GenerationHost=OR-UTIL02
*/

instance of MSFT_RoleResource as $MSFT_RoleResource1ref
{
ResourceID = "[WindowsFeature]snmp";
 SourceInfo = "::12::9::WindowsFeature";
 Name = "SNMP-Service";
 ModuleName = "MSFT_RoleResource";
 ModuleVersion = "1.0";
};

instance of MSFT_RoleResource as $MSFT_RoleResource2ref
{
ResourceID = "[WindowsFeature]Web";
 SourceInfo = "::25::29::WindowsFeature";
 Name = "web-Server";
 ModuleName = "MSFT_RoleResource";
 ModuleVersion = "1.0";
};

instance of OMI_ConfigurationDocument
{
 Version="1.0.0";
 Author="smurawski";
 GenerationDate="10/08/2013 16:03:51";
 GenerationHost="OR-UTIL02";
};

And Server2 has the reverse.

/*
@TargetNode='Server2'
@GeneratedBy=smurawski
@GenerationDate=10/08/2013 16:06:31
@GenerationHost=OR-UTIL02
*/

instance of MSFT_RoleResource as $MSFT_RoleResource1ref
{
ResourceID = "[WindowsFeature]snmp";
 SourceInfo = "::13::9::WindowsFeature";
 Name = "SNMP-Service";
 ModuleName = "MSFT_RoleResource";
 ModuleVersion = "1.0";
};

instance of MSFT_RoleResource as $MSFT_RoleResource2ref
{
ResourceID = "[WindowsFeature]FileSharing";
 SourceInfo = "::20::29::WindowsFeature";
 Name = "FS-FileServer";
 ModuleName = "MSFT_RoleResource";
 ModuleVersion = "1.0";
};

instance of OMI_ConfigurationDocument
{
 Version="1.0.0";
 Author="smurawski";
 GenerationDate="10/08/2013 16:06:31";
 GenerationHost="OR-UTIL02";
};

To highlight a neat trick since we are using a switch statement and switch can process collections, we can specify more than one role in our hashtable and our configuration should be able to add all the required resources.

$ConfigurationData = @{
  AllNodes = @(
    @{NodeName = 'Server1';Role='Web'},
    @{NodeName = 'Server2';Role='FileShare'}
    @{NodeName = 'Server3';Role=@('FileShare','Web')}
  )
}

configuration MyFirstServerConfig 
{
    node $allnodes.NodeName
    {
        WindowsFeature snmp
        {
            Name = 'SNMP-Service'
        }       
        switch ($Node.Role)
        {
            'FileShare' {
                            WindowsFeature FileSharing
                            {
                                Name = 'FS-FileServer'
                            } 
                        }
            'Web'       {
                            WindowsFeature Web
                            {
                                Name = 'web-Server'
                            } 
                        }
        }
    }
}

MyFirstServerConfig -ConfigurationData $ConfigurationData

If we look at the configuration generated for Server3, we’ll find both Web-Server and FS-FileServer roles described.

/*
@TargetNode='Server3'
@GeneratedBy=smurawski
@GenerationDate=10/08/2013 16:06:31
@GenerationHost=OR-UTIL02
*/

instance of MSFT_RoleResource as $MSFT_RoleResource1ref
{
ResourceID = "[WindowsFeature]snmp";
 SourceInfo = "::13::9::WindowsFeature";
 Name = "SNMP-Service";
 ModuleName = "MSFT_RoleResource";
 ModuleVersion = "1.0";
};

instance of MSFT_RoleResource as $MSFT_RoleResource2ref
{
ResourceID = "[WindowsFeature]FileSharing";
 SourceInfo = "::20::29::WindowsFeature";
 Name = "FS-FileServer";
 ModuleName = "MSFT_RoleResource";
 ModuleVersion = "1.0";
};

instance of MSFT_RoleResource as $MSFT_RoleResource3ref
{
ResourceID = "[WindowsFeature]Web";
 SourceInfo = "::26::29::WindowsFeature";
 Name = "web-Server";
 ModuleName = "MSFT_RoleResource";
 ModuleVersion = "1.0";
};

instance of OMI_ConfigurationDocument
{
 Version="1.0.0";
 Author="smurawski";
 GenerationDate="10/08/2013 16:06:31";
 GenerationHost="OR-UTIL02";
};

Next Up

In the next post, we’ll continue this topic and look at other ways we can parameterize configurations as well as nesting configurations.  We’ll also touch on how to apply these configurations from Start-DscConfiguration and via a Pull Server.  Stay tuned!

Building a Desired State Configuration Pull Server


Quick recap, I’m working through a series of posts about the Desired State Configuration infrastructure that I’m building at Stack Exchange, including some how-to’s.

The High Points

I started with an overview of what and why.  Today, I’m going to start the how.

Building a Pull Server

I’m going to describe how to do this with Server 2012 R2 RTM (NOTE: this is not the General Availability  release, so there may be changes at GA), since that’s the environment I’m working most in.  If there is enough demand, I may follow up with how to do this using the Windows Management Framework on downlevel operating systems after the GA version of WMF 4 is released.

The first step is adding the required roles and features, including the DSC Service.

Add-WindowsFeature Dsc-Service

Fortunately, the Dsc-Service feature has the right dependencies configured so IIS, the correct modules, and the Management OData Extension are all enabled.

Next we need to set up the IIS web site:

  • Create an directory to serve the web application from (I’ll use c:\inetpub\wwwroot\PSDSCPullServer)
  • Copy several files from $pshome/modules/psdesiredstateconfiguration/pullserver (Global.asax, PSDSCPullServer.mof, PSDSCPullServer.svc, PSDSCPullServer.xml) to this directory.
  • Copy PSDSCPullServer.config and rename it to web.config
  • Create a subdirectory named “bin”.
  • Copy one file from $pshome/modules/psdesiredstateconfiguration/pullserver (Microsoft.Powershell.DesiredStateConfiguration.Service.dll) to the “bin” directory.
  • In IIS, create an application pool that runs under the “Local System” account.
  • In, IIS, create a new site (or application in an existing site or just use the existing default site)
  • Point the site or application root to the directory you designated as the root of the site.
  • Unlock the sections of the web config as below
$appcmd = "$env:windir\system32\inetsrv\appcmd.exe" 
& $appCmd unlock config -section:access
& $appCmd unlock config -section:anonymousAuthentication
& $appCmd unlock config -section:basicAuthentication
& $appCmd unlock config -section:windowsAuthentication

 

Now we need to set up the location where the pull server content will be served from.  Installing the DSC Service feature creates a default location ( $env:programfiles\WindowsPowerShell\DscService ).  There’ll you find sub-directories for configuration and modules.  We can use these folders or we can create another location.  I’m going to stick with the defaults for now.  We’ve got a few steps left.

First, we need to copy the Devices.mdb from $pshome/modules/psdesiredstateconfiguration/pullserver to the root of our pull server data location (in this case, $env:programfiles\WindowsPowerShell\DscService )

Update the web.config app settings with the following settings:

<add key="dbprovider" value="System.Data.OleDb" />
<add key="dbconnectionstr" value="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\Program Files\WindowsPowerShell\DscService\Devices.mdb;" />
<add key="ConfigurationPath" value="C:\Program Files\WindowsPowerShell\DscService\Configuration" />
<add key="ModulePath" value="C:\Program Files\WindowsPowerShell\DscService\Modules" />

After that your pull server should be up and running.  You should see something like this if you navigate to http://yourpullserver/psdscpullserver.svc

PullServerDefaultUrl

 

 

Building a Desired State Configuration Infrastructure


This is a the kickoff in a series of posts about building a Desired State Configuration (DSC) infrastructure. I’ll be leveraging concepts I’ve been working on as I’ve been building out our DSC deployment at Stack Exchange.

The High Points

I’m starting today with the general overview of what I’m trying to accomplish and why I’m trying to accomplish this. The what and why are critical in determining the how

The Overview

Goal:

All systems have basic and general purpose roles configured and monitored for drift via Desired State Configuration.

Reason:

System configuration is the one of the silent killers for sysadmin (yes, I prefer sysadmin to IT Pro – deal with it). In the case where deployments are not automated, each system is unique, a snowflake that results from the our fallibility as humans.

The more steps involved that require human intervention allow for more potential failure points. Yes, if I make a mistake in my automation, then that mistake can be replicated out. But as Deming teaches with the Wheel of Continuous Improvement (Plan, Do, Check, Act),  we can’t correct a process problem until we have a stable process.

Deming Cycle

Every intervention by a human adds instability to the equation, so first we need to make the process consistent. We do that by standardizing the location(s) of human intervention.  Those touch points become the areas that we can tweak to further optimize the system.  I’m getting a bit ahead of myself though.

Let’s continue to look at how organizations tend to deploy systems.  Organizations tend to have several levels of flexibility in their organizations about how systems are built and provided for use.  The three main categories I see are:

  • Automated provisioning from a purpose built image
  • Install and configure from checklist
  • Install and configure on demand

Usually, the size of the organization tends to indicate to what level they’ve automated deployments, but that is less true today.  Larger organizations tend to have more customized and automated deployments.  It’s mainly been a matter of scale.  With virtualization and (please forgive me) cloud infrastructures, even smaller organizations can have ever increasing numbers of servers to manage, with admin to server ratios of 1 to hundreds being common and where the number of servers starts to overtake the client OS count.

If we aren’t in a fully automated deployment environment, each server has the potential to be subtly (or not so subtly) unique.  Checklists and scripts can help with how varied our initial configurations can start out, but each server is like a unique piece of art (or a snowflake).

Try to make more than one of me…

That’s kind of appealing to sysadmins who like to think of themselves as crafters of solutions.  However, in terms of maintainability, it is a nightmare.  Every possible deviation in settings can cause problems or irregularities in operations that can be difficult to track down.  It’s also much more work overall.

What we want our servers to be is like components fresh off the assembly line.

Keeping it consistent

Each server should be consistently stamped out, with minimal deviations, so that troubleshooting across like servers is more consistent.  Or, even more exciting, if you are experiencing some local problems, refreshing the OS and configuration to a known good state becomes trivial.  Building the assembly line and work centers can be time consuming up front, but pays off in the long haul.

My Situation:

At Stack Exchange, we are a mix of these categories.  All of our OS deployments are driven by PXE boot deployments.  For our Linux systems, we fall into the first group.  We can deploy an OS and make the addition to our Puppet system, which will configure the box for the designated purpose.  For our Windows systems, we operate out of the second and third groups.  We have a basic checklist (about 30-some items) that details the standards our systems should be configured with, but once we get to configuring the server for a specific role, it’s been a bit more chaotic.  As we’ve migrated to Server 2012 for a web farm and SQL servers, we’ve began to script out our installations for those roles, so they were kind of automated, but in a very one-time run way.

Given where we stood with our Windows deployments and the experience we had with Puppet, we looked at using Puppet with our Windows systems (like Paul Stackpodcast, video) and decided not to go that route (why is probably worthy of another post at another time).  That was around the time that DSC was starting to peek it’s head out from under the covers of the Server 2012 R2 preview.  Long story made short, we decided to use DSC to standardize our Windows deployments and bring us parity with our Linux infrastructure in terms of configuration management.

Proposed Solution: Desired State Configuration

DSC offers us a pattern for building idempotent scripts (contained in DSC resources) and offers an engine for marshaling parameters from an external source (in my case a DSC Pull Server, but could be a tool like Chef or some other configuration management product) to be executed on the local machine, as well as coordinating the availability of extra functionality (custom resources).  I’m building an environment where a deployed server can request it’s configuration from the pull server and reduce the number of touch points to improve consistency and velocity in server deployments.

Next up, I’m going to talk about how I’ve configured my pull server, including step by step instructions to set one up on Server 2012 R2.

Need Desired State Configuration Modules?


You’ve probably been hearing about Desired State Configuration from a number of sources (Runas Radio, the PowerScripting Podcast, or the Channel 9 TechEd video for example).  If you haven’t go check out those previously mentioned resources, I’ll wait…

Ok, now that you have a basic understanding of what Desired State Configuration (DSC) is, I have an announcement.

PowerShell.Org is building a repository of DSC modules for the community to use and contribute to.

As I’ve started working with Desired State Configuration, I began building up a repository of modules I would use in configuring my systems.  I started to round them out with some basic documentation and decent logging messages and began pushing them to GitHub.

I’ve also seen several others starting to post some DSC modules on Github and elsewhere.  Since we are very early in the Desired State Configuration lifecycle (it’s still not RTM yet), I would like our community to come together on a central location for our community contributions.  I reached out to Don and the PowerShell.Org team and they graciously offered to host the contributions on the PowerShell.Org GitHub repository.  What that means is that this effort is no longer under the control of one person (me), but owned by the community, by PowerShell.Org.

There’s not much in the repository yet, so if you’ve been experimenting with DSC and would like to share your efforts with the community, feel free to send a pull request (if you’re into the whole GitHub thing) or file an issue on the GitHub site and we’ll figure something out.

There is some basic “Getting Started With Developing DSC Modules” information at the GitHub repository as well.