Summary: Learn how to use Chef and DSC in Windows PowerShell to deploy System Center in this guest blog post by Jason Morgan.
Ed Wilson, here. Today I have a guest blog post by Jason Morgan in which he will talk about using Desired State Configuration (DSC) and Chef to deploy System Center. Welcome back, Jason…
Hello again. Today I’m writing to analyze the deployment of System Center by using Desired State Configuration and Chef. I’ve been using Chef heavily for the last six months because I decided that using DSC to run my System Center deployments was a too heavy lift on its own. Adding Chef has allowed me to run my System Center builds from my local lab through my test, UAT, and production environments with a single repository.
Likewise, DSC has provided me with a simple and common interface for dealing with Windows Server, which I’ve found greatly expands the administrative surface for Chef (where those resources are somewhat limited).
DSC and System Center
I’ve been taking advantage of the resources provided by Microsoft to deploy some common System Center components: SCOM, SMA, and Service Manager. In this regard, xSCOM and xSCSMA do a pretty good job for SCOM and SMA, but there is no such resource for Service Manager.
For all the System Center components I use the Group, User, Package, and WindowsFeature resources to configure the prerequisites. You can write loops in Chef to handle adding these prerequisites. The resources are called in almost the same way as in PowerShell. Here’s a quick example:
['NLB','RSAT-NLB'].each do |feature|
dsc_resource feature do
resource :WindowsFeature
property :Name, feature
property :Ensure, 'Present'
end
end
Here I take an array of two strings, NLB and RSAT-NLB, and I assign them to a variable named Feature and loop through the WindowsFeature DSC resource.
Chef lets you store information such as users and passwords in searchable repositories called “data bags.” You can encrypt data bag items and make use of Chef’s own security system to decrypt data bag items on the individual nodes. This makes the distribution of account information especially simple. Here’s an example of searching through the items in the user’s collection with a name matching myapp and adding them to the admin group:
search('users',"id:*myapp*").each do |admin|
dsc_resource "Administrator #{admin['id']}" do
resource :Group
property :GroupName, 'Administrators'
property :Ensure, 'Present'
property :MembersToInclude, ["#{node['serverBase']['netbiosName']}\\#{admin['id']}"]
property :Credential, ps_credential("#{node['serverBase']['netbiosName']}\\#{install['id']}",install['password'])
end
end
You have the option to store non-standard resources with your cookbooks if they are not too large. Personally, I archive any non-standard resources along with the actual binaries I need for the installations. I maintain the archive within a web server built for each environment. Here is an example of using a native Chef resource, remote_file, to download the WMF5 update and store it locally:
remote_file "#{ENV['SYSTEMDRIVE']}\\Win8.1AndW2K12R2-KB3066437-x64.msu" do
source "https://download.microsoft.com/download/3/F/D/3FD04B49-26F9-4D9A-8C34-4533B9D5B020/Win8.1AndW2K12R2-KB3066437-x64.msu"
end
There are two key takeaways here:
- When you reference Windows paths in Chef recipes, you need to use \\ in place of \. Ruby sees the \ as the escape character, so you need one to escape the other.
- Chef recipes can leverage environment variables with ENV[‘MyVariableName’].
As a natural segue, Chef provided me with significant benefits when working with environments. Ingredients like accounts, urls, ip schemes, and even the number of nodes in a particular app vary by environment. Chef allows you to use the environment to override values in a recipe. I won’t go any deeper into the discussion about environments here, but it is a topic you should become very familiar with if you’ll be using Chef.
Operations Manager
Operations Manager is pretty straightforward. After the prerequisites are installed, all you need to do is call the resource, for example:
dsc_resource 'WebConsole' do
resource :xSCOMWebConsoleServerSetup
property :Ensure, 'Present'
property :SourceFolder, 'SCOM'
property :SourcePath, "#{ENV['SYSTEMDRIVE']}\\Sources"
property :SetupCredential, ps_credential("#{node['serverBase']['netbiosName']}\\#{admin['id']}",admin['password'])
property :ManagementServer, node['scom']['mgmtServer']
notifies :reboot_now, 'reboot[now]', :immediately
end
One thing worth mentioning is that when you use pscredential in a recipe, Chef provides a function to build the credential object:
ps_credential([string]username,[string]password)
Service Management Automation
Service Management Automation (SMA) is three resources for a full standalone instance:
- SCSMAWebServiceServerSetup
- xSCSMARunbookWorkerServerSetup
- xSCSMAPowerShellSetup
Again, when the prerequisites are in place, it’s a pretty quick installation to set up a standalone server. Additionally, it’s really nice to use Chef to install the correct modules on all my runbook servers.
dsc_resource 'SMAPS' do
resource :xSCSMAPowerShellSetup
property :Ensure, 'Present'
property :SourcePath, "#{ENV['SYSTEMDRIVE']}/Sources"
property :SetupCredential, ps_credential("#{node['serverBase']['netbiosName']}\\#{smaSetupAccount['id']}",smaSetupAccount['password'])
property :SourceFolder, 'SMAInstall'
end
Service Manager
Service Manager is a slightly different beast. As of this writing, there are no DSC resources for deploying it. Luckily, Chef has a pretty robust PowerShell_script resource, which is a lot easier to use than the DSC script resource (in my experience). You need to only write a script, put it in a string, and insert variable substitutions to change whatever you need to change.
When you are going to use it, be sure you set up a guard condition—a “not_if” or “only_if” script, which will allow your resource to be idempotent.
I had a big issue with Service Manager’s installation script. Start-Process, which I used to run the installation, wouldn’t let me swap the user running the setup. It made it difficult to run unattended at first. As with everything else in this process, Chef had a pretty easy answer for this issue. I set up the Chef client to run as a task under the appropriate account and let it run.
Here’s an example of using the PowerShell_script resource to provision disks:
powershell_script 'multiple disks' do
code <<-MYCODE
$disks = get-disk | where {$_.OperationalStatus -match "Offline"}
foreach ($d in $disks)
{
$d | Initialize-Disk -Passthru | New-Partition -AssignDriveLetter -UseMaximumSize | Format-Volume -confirm:$false
}
MYCODE
only_if '(get-disk | where {$_.OperationalStatus -match "Offline"}).count -ge 1'
end
ruby, the language Chef uses, lets you setup multi line strings like powershell’s here string. The syntax is like:
<<-STRING
some text
STRING
You can wrap longer PowerShell scripts in a multiline string construct and add it as an argument for the code parameter on the PowerShell_script resource. The only_if parameter ensures that the script won’t run unless it’s necessary. Only_if and Not_if in the PowerShell script resource respond to a Boolean value returned by the script.
Why add Chef?
I wanted to highlight why I added Chef on top of DSC. Chef has some really cool functionality, which allowed me to speed up my development cycles and releases. Here are some of the key features:
Handling password distribution
One of the major issues I had with DSC on its own was securely distributing passwords. Chef made that pretty simple. You can encrypt data bag items, and by using a cool feature called chef-vault, you can use the same certificates that Chef distributes automatically to decrypt those items from appropriate clients.
Distributing resources
Because Chef executes each resource independently of the others, it lets me use DSC to copy and extract DSC resources. This was always one of my major issues with DSC.
Running PowerShell scripts
If you’ve tried to use the DSC script resource a lot, you’ve likely run into issues expanding variables. The DSC script resource won’t let you reference variables from the broader configuration without a lot of messing around. It’s complicated and hard to use. With Chef, it’s simple. I write the script and use Chef’s variable system to insert values as needed.
Running as a task
DSC runs as a task that runs as the system. With Chef, you have options: you can run as a service or as a task. That task can then be configured to run with all the arguments of any other task. I can swap the user or the execution time, and the task is simple and straightforward. Don’t get me wrong, the LCM is awesome, but I definitely don’t miss using it.
Environments
This is one of the things that most attracted me to Chef. Having the ability to set certain parameters that change depending on your environment (test/dev/uat/prod) is incredibly valuable. I write one deployment configuration (“recipe” in Chef speak), and it changes its characteristics based solely on the environment it’s in. Ultimately, I swapped from using only DSC to using DSC and Chef together when I found myself writing my own environment system.
Order of operations
Resources are always executed in order and, unless otherwise specified, a failure in a resource stops execution of the client immediately. Always knowing where you had an issue greatly simplifies any needed troubleshooting. Another benefit is that the Chef client writes the details of its last run in a log, enabling you to review the details of past runs at your convenience.
~Jason
Thank you, Jason, for a way cool blog post.
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