Practical exploration of Chef and Microsoft PowerShell Desired State Configuration (DSC)Bob van den Heuvel
In my daily work I use Chef for managing MS Windows infrastructures. For this reason I keep a close look on developments of Chef on MS Windows technologies. One of these technologies which Chef is adopting is PowerShell Desired State Configuration (DSC). Building an Active Directory (domain controller) using this new combination of technologies drew my specific attention. This proved to be a very interesting excercise since it gave me valuable insight into the possibilities and differences of how Chef interacts with DSC, and how I seem to have a problem with the 'code' attribute of 'dsc_script'.
By writing this blog I hope to help others gain insight into Chef and DSC as well. And please return the favor by sharing your thoughts on this blog or Chef combination with DSC in general.
The practical goal of this exploration, or walk-though, is to install Active Directory using Chef and Desired State Configuration (DSC).
A note upfront to security/food critics; to be able to provide an actual working recipe and keep focus on the exploration of the Chef DSC resources, many food and security rules are ignored.
In terms of choosing Chef/DSC resources, preference of resource usage could be:
- dsc_resource (requires the Community preview DSC cookbook)
- dsc_script, using code attribute
- dsc_script, using external PowerShell file specified by command attribute
'dsc_resource' might look and feel most 'Chef-native'; just a resource with attributes.
'dsc_script' might be my personal favorite, as it allows me to put native DSC code directly into the resource/recipe, if only it wasn't for the limitation discussed in this blog.
The actual recipe, which works on my machine ;), can be found here.
Information on the respective DSC Resource xADDomain:
In any case knowledge on the respective DSC resource, specifically the properties, is required. In this case the resource xADDomain;
This output provides us with the (required) parameters, and their respective names.
Chef resource dsc_resource
When defining the Chef resource 'dsc_resource', based on the information on the DSC resource 'aADDomain', the 'dsc_resource' should look like:
This presents a typecasting problem; what is the Ruby equivalent for PSCredentials?
According to the RFC, there are direct mappings for "simple" types (string, numbers, booleans), but nothing specifically for PSCredentials. Perhaps there might be advanced magic available (ab)using NilClass or 'Chef::Resource::DscResource', but no obvious suspect to me.
So, for now at least, dsc_resource does not seem to be the right candidate when PSCredentials are required.
Chef resource dsc_script
The 'dsc_script' resource either takes inline a (subset!) DSC code block specifying the DSC resources, via the code attribute or a complete DSC Configuration definition in a separate file.
As the DSC code is interpreted by PowerShell, the opportunity presents itself to also specify regular PowerShell code, such as cmdlets which can make a PSCredentials object from two strings which contain username and password.
Obviously this should ring security bells...
The Chef recipe code below assigns the DSC code block, which include the PowerShell statements to create PSCredentials, to a variable named 'dsc_code_script':
This code (sub) block of DSC will remain the same when using either the code or command attribute. By assigning this block to a variable, hopefully this keeps the next steps more clear.
Chef resource dsc_script, using code attribute
With the dsc code specified earlier in the 'dsc_code_script' variable, the dsc_script should simply be:
But alas, the Chef run fails on:
Non-natively-installed DSC resources, such as installed by the Resource Kit, need to be imported into the DSC session.
The Import-DscResource keyword(!) (not a PowerShell cmd-let) needs to be specified at a level in DSC Configuration, which is located (scoped) outside of where the chef dsc_script code property is placed.
For this reason, we need to control the entire DSC Configuration specification and need to move on to using the command attribute.
(Reason that 'dsc_resource' did not fail, is that it automatically creates the required/respective import directive code.)
Chef resource dsc_script, using command attribute
The command attribute needs the path to a file containing the complete DSC configuration specification.
Part of this complete DSC Configuration code, is the same DSC specification of resources and password, which was assigned to the variable 'dsc_code_script'. To also illustrate the positioning of that code in the DSC Configuration file, this previously assigned variable will be re-used in the ruby code below:
This code block illustrates the functional difference between the 'dsc_script' attributes 'code' and 'command'; when using the 'code' attribute, everything except the Import-DscResource is placed around that code.
As the 'Import-DscResource' directive must be specified outside the 'node' block, this demonstrates that using non-"natively-provided" DSC resources will not work when using the 'code' attribute.
Having "solved" this, the 'dsc_scrip't using the 'command' attribute should be:
Alas, next stop:
When creating the MOF file from the DSC resource, the credentials are translated to text parameters. You should, and can, only do this if you understand the consequences of this. One being that an actual (MOF) file on the filesystem is created which stores the password in plaintext!
That being said, for this example we will give our consent by creating a DSC configuration data block:
And provide the configuration data block to the dsc_script:
When revisiting the Chef/DSC resources, with the proposed preference order of usage:
- dsc_resource; easy interface to most DSC resource, installed as add-on or not
- dsc_script, using code attribute; very handy if some inline PowerShell magic is required, but not available for non-standard DSC resources
- dsc_script, using external PowerShell file specified by command attribute; complete control and most DSC feature rich
As learned from bullet number 2, it seems that only the DSC resources which are "natively" available, can be used via the inline coding using the `code` attribute. This adds up to about 12 resources. The DSC Resource Kit adds about 120 resources on top of that, which cannot be used with inline code.
Either I'm mistaken, or this should be fixed, to my humble opinion. Perhaps this will be fixed in the near future (see issue report at chef).
While working through the example, a little bit of cheating was done by not mentioning that we need a feature to be installed before we create a domain. In the end result below this is included, and also creation and removal of the temporary file, and a reboot after the installation of Active Directory.
For this example the following runlist was used:
- windows (the Community standard windows cookbook)
- dsc (the Community preview DSC cookbook)
- dsctst::xADomain_test (test cookbook with the recipe below)
In this case the Windows box was provisioned with Vagrant, and credentials as specified in the recipe.