Over the last year I have really started playing with Desired State Configuration. This is another player in the increasingly crowded ‘Infrastructure as Code’ market. Other major players being Ansible, Salt, Chef, and Puppet, to name a few.
I remember when I was researching some of the different available tools I ended up searching for ‘dsc vs chef’ which resulted in me finding this tweet from Jeffrey Snover (the creator of Powershell/DSC)
It took awhile for me to understand what that meant…until I started using DSC. The power of DSC comes from the numerous resources, the idempotence, and its flexibility (thanks to Powershell). And if you know Powershell, it’s very easy to write your own custom DSC resources.
However, the actual execution of DSC is where it falls flat. DSC has no real tooling surrounding it. Push mode is straightforward and works OK (though it is seriously lacking in a few areas) but the Pull Server situation is downright terrible.
DSC resources are likely what’s under the hood when it comes to managing Windows nodes in the future, but we have yet to know what tooling will be used in providing the delivery, reporting, and lifecycle of these resources.
This is a quote from Microsoft when they introduced DSC support for Puppet
We view DSC as a platform technology to manage Windows, and this technology can be extended with the creation of new resources as well as the reuse of PowerShell DSC functionality in different solutions. As Microsoft partners make their components configurable via DSC, their customers will be able to choose from a wide range of configuration management tools that support DSC.
Right now there are numerous configuration management tools, primarily Linux -centric, which have begun to transition their Windows strategy from developing their own resources to supporting DSC.
This post is about using one of these tools (Chef), to manage Windows nodes with DSC. I chose Chef mostly because I just like Chef. I also chose it because I think it is one of the strongest players in cloud automation (see AWS Opsworks for Chef Automate) and because I think it’s the best of these tools when it comes to the entire CI pipeline (Chef Automate is awesome)
But I do want to point out that all of the other tools I listed above (Ansible, Salt, Puppet) also support managing Windows nodes and also let you utilize DSC in some fashion.
Bootstrapping a Windows node
I’ve installed the ChefDK on my Windows workstation so I will be doing everything from within Powershell. In case you want to do the same, just grab the ChefDK for Windows from here and follow the steps for Windows here. Or just use whatever workstation you want. Obviously there is no requirement to bootstrap Windows nodes from a Windows workstation.
Another important thing to note is firewall rules. Unlike bootstrapping *nix nodes over SSH, you will typically bootstrap Windows nodes by using WinRM (you can use SSH too). Make sure you have the right ports open (the default is 5985).
I’m working within my existing Chef repository on this workstation and I’ll be bootstrapping a random Windows Server 2012R2 node with an IP address of 10.0.3.203 and a FQDN of sfbfe02.mhickok.me.
I’ll use knife bootstrap windows to actually do the bootstrapping (for a full list of options see the knife bootstrap windows documentation):
knife bootstrap windows winrm 10.0.3.203 -x DOMAIN\my_user -P 'some_password' -N sfbfe02.mhickok.me
I did not assign any run lists at this point so the bootstrap process will be very quick.
Once complete, I can look in Chef Manage to see the new node
Or if you aren’t using Chef Manage you could run a query to find all Windows nodes
And with the node now being managed by Chef, we can write our first recipes.
Chef Recipes and DSC
You don’t have to use DSC in order to use Chef. There are existing resources such as ‘windows_package’, ‘windows_service’, and more, which allow you to author Cookbooks/Recipes without caring about DSC.
However, I think DSC will play a large role in the future of Windows systems administration and try to use it where I can. It also allows you to use an existing custom DSC resource (not composite resources though) and allows you to continue to leverage your DSC development which can then be used with another tool later down the road.
For example, while you could be using Chef right now as your configuration management platform, you may want to switch down the road. When that happens you want to make sure that the work you have invested is not wasted. And since nearly all configuration management platforms support DSC, it’s a very safe bet as those resources will likely work regardless of your tooling.
I’ll get started by generating a new cookbook. I’ll just be making a cookbook for one of my random utility servers which could serve a number of purposes in a lab environment (print server, Group Policy, AD DS, and Hyper-V Management, and FTP server).
Since this demonstration is about utilizing DSC with Chef and not Chef best practices, I’m just going to shove all of this into a single recipe (the default recipe). I’ll start with the features that I want to install
(you can see the combined sample here)
# Cookbook:: utility # Recipe:: default # # Copyright:: 2017, The Authors, All Rights Reserved. [ 'RSAT-ADDS-Tools', 'Hyper-V-Tools', 'GPMC', 'Web-Mgmt-Tools', 'Web-Mgmt-Console', 'Web-Ftp-Server', 'Print-Server' ].each do |feature| dsc_resource 'Ensure Utility Server Components Installed' do resource :WindowsFeature property :Name, feature property :Ensure, 'Present' end end
Just like with Powershell DSC, you can utilize loops within the recipe to go through an array of items you want to do something with (with Ruby rather than DSC obviously).
If you have used DSC this will look pretty familiar. You specify the resource type and then everything else is a property. To show what it looks like when doing a single feature, I’ll make sure that this server never has the Windows Backup feature enabled
dsc_resource 'Ensure Windows Backup is not Installed' do resource :WindowsFeature property :Name, 'Windows-Server-Backup' property :Ensure, 'Absent' end
I also know that these utility servers will be used as web servers for various purposes which I will likely want to manage with Chef at a later point. For now, I just want to make sure that the xWebAdministration module is installed so that I can manage it with those resources later (like xWebSite).
To install that I can use PowerShellGet by utilizing the powershell_script chef resource
powershell_script 'Install xWebAdmin' do code 'Install-Module -Name xWebAdministration -SkipPublisherCheck -Force' only_if '!(Get-Module xWebAdministration -ListAvailable)' end
If the xWebAdministration module is not on the destination node, it will be installed.
Now I want to make sure that the FTP and Print services are enabled automatically and are always in the running state
dsc_resource 'Ensure FTP Service is automatic and running' do resource :Service property :Name, 'FTPSVC' property :StartupType, 'Automatic' property :State, 'Running' end dsc_resource 'Ensure Print Service is automatic and running' do resource :Service property :Name, 'Spooler' property :StartupType, 'Automatic' property :State, 'Running' end
The syntax is very similar to writing native DSC but slightly adapted to conform to a standard Chef resource.
Quick note on dsc_script vs dsc_resource
The Chef resource being used later is the dsc_resource. There are two ways to use DSC with Chef, dsc_resource and dsc_script. In general you want to use dsc_resource when possible but it does have the requirement of WMF 5 (shipped with Server 2016) or higher unlike dsc_script which only requires WMF 4 (shipped with Server 2012).
There are a number of reasons for choosing dsc_resource, but just know that dsc_resource is the direction which Chef is heading.
Assigning and executing the Run List
As with any cookbook, I’ll want to upload it to the Chef server with knife
knife upload cookbook utility
Then I’ll add the recipe to the node’s run list (I can just assign the cookbook since I used the default recipe)
knife node run_list add utility.mhickok.me 'recipe[utility]'
And if I look at the node I can see the updated run list
knife node show utility.mhickok.me
Lastly I can force a client update by running the Chef client on the node
knife winrm 'name:utility.mhickok.me' chef-client --winrm-user DOMAIN\my_user --winrm-password 'my_password'
This will start the run through the recipe on the node
The Windows node will run through the run list just like on any other platform, meaning it runs top to bottom.
It starts by running through the array of features to install but the client errors out. The nice thing is that not only do I see which part of the recipe failed, but I also get to see the actual Powershell exception that was thrown
And sure enough if I look in the recipe I had the wrong name for the FTP feature. So I updated the recipe, re-uploaded the cookbook, and performed another client run
It updated the resources as needed and completed successfully.
What about Configuration Data?
DSC lets you use something called Configuration Data as a way of separating the logic of which nodes are getting which configurations. It’s an attempt to introduce some form of modularity into DSC (and it does) but ends up being incredibly confusing when compared to other tools’ implementation of modularity and re-use.
It’s actually funny that when Don Jones introduces the concept of the Configuration Data file in ‘The DSC Book’ he felt the need to bring up this up again
I feel it’s important at this stage to remind you that DSC is a platform, not a solution set. That is, there’s no tooling.
He then gives his best shot at explaining it and readers are still left confused. Fortunately, Chef can provide some of that tooling functionality and ‘Roles’ are what can be used to replace a lot of what ‘Configuration Data’ is used for.
I’m not going to dive into Chef Roles, but just know that assigning a single recipe to the run list of a single node is not how you would actually do this.
Typically you would create roles for your server types, have a few different recipes within a cookbook, and have the role auto-assigned during your provisioning process. My point is that it’s much easier than dealing with Configuration Data in DSC.
Conclusion and Additional Resources
As I mentioned at the beginning, Chef and DSC are not competitors but rather compliments to each other. Using Chef as the tooling surrounding DSC makes DSC easier to actually use. And using DSC as the platform for managing your Windows nodes brings huge benefits to Chef.
If you are interested in learning more Chef and DSC– there are lots of resources out there. Check out the Chef subreddit, request an invite to this amazing Slack channel, or just check out the Chef docs.