This is post 1 of 7 in a series titled Development environments with PowerShell DSC
- Development environments with PowerShell DSC – Introduction to DSC
- Development environments with PowerShell DSC – Windows Features
- Development environments with PowerShell DSC – Mongo DB
- Development environments with PowerShell DSC – SQL Server
- Development environments with PowerShell DSC – Sitecore
- Development environments with PowerShell DSC – Coveo CES
- Development environments with PowerShell DSC – Coveo REST API & Coveo for Sitecore
I've written before about approaches to automating the install of Sitecore instances via PowerShell, but recently I've been working upgrading this process to set up entire servers. As part of this research I've been working on how to move the scripting over to PowerShell Desired State Configuration – Microsoft's framework for automating the configuration of servers. Having got to a position where the scripts are working and I can turn a plain copy of Windows Server into a functioning Sitecore box, I thought I'd shared an explanation of the tools and how it can be used for Sitecore development environments.
I'm going to break this up into a series of posts, as it's quite a big topic. This week is a bit of an introduction to DSC and my goals for it:
It's a scripting framework for automating the configuration and setup of servers. If you've ever used Chef or Salt then this is basically Microsoft's equivalent. If you haven't then DSC is an extension to PowerShell v4 and above that gives you two extra things:
DSC is designed to support the DevOps concepts of automating infrastructure tasks and having executable code for describing server state which can also be seen as documentation of the state you require. It allows you to move towards the idea of having servers being disposable – since the process of building them is automated, creating and destroying them is easy. You can get to a state where you click a button in the management tools for Hyper-V (or your preferred Virtualisation framework) to spin up a new virtual machine and it automatically pulls the right configuration from a central location and configures itself to meet your requirements.
Here's a very basic script, which can be used to install IIS onto a machine:
Configuration EnableIIS { Node “WebSvr001" { WindowsFeature WebServer { Name = "Web-Server" Ensure = "Present" } } } EnableIIS
The outer block declares a
Configuration
. That's basically naming the DSC script. Any valid PowerShell identifier can be used here. The name is important because (under the surface) what you're declaring here is kind of like a special CommandLet – hence we need the name so we can execute it later.
The second block declares a
Node
. Nodes describe the name of a server (or servers) to which the script is relevant. Here it's hard coded to a machine named "WebSvr001", but commonly it's declared as an expression so that one Configuration can be applied to many servers. You can also declare multiple Nodes inside one Configuration, for example to declare a script for a cluster of servers.
The inner block describes a "resource" – which is what DSC calls the instructions for a particular bit of state you want to affect. In this case we have a
WindowsFeature
resource, which knows how to install a feature of Windows. Again these need a name, and the names of resources are used to allow us to specify dependencies in more complex scripts. Usually a script will have many resources, describing the set of configuration changes required. Resources also take parameters, and here we're specifying the
Name
of the feature to install ("Web-Server" is the feature name for IIS) and with
Ensure
we're saying that DSC should make sure that this feature is present. One of the benefits of DSC is that it is state-aware, so if IIS was already installed this script would do nothing. And (obviously) you could also construct a resource whose job was to remove a windows feature if you wanted.
In effect, the script describes the same process as logging on to a server, firing up the Server Manager and using its UI for Roles & Features to install IIS.
There are lots of resource types available as part of DSC. You can unzip archives, install programs, control services, create files and directories, copy files and directories, add users to Active Directory and run raw PowerShell by default. But you can also extend DSC with your own specialised resources – and there are lots available on the Internet to make use of as well.
The final line of the script above uses the name of our
Configuration
to invoke the script. So what happens when we run this? Well interestingly, it doesn't actually change the state of any servers. Two things happen:
Firstly, the PowerShell DSC framework parses your script and does a syntax check. If you've made any errors then you get an error explaining what it thinks is wrong.
Secondly, the script is translated. The Windows Management framework can't read the PowerShell DSC scripts directly. It works on a cross-platform standard for describing server state called the "Managed Object Format". So DSC translates your script into the appropriate
.MOF
files – one for each server you described in your
Node
blocks. It saves these files into a folder named for your
Configuration
.
These files contain basically the same data as your DSC script, just in a format that's more focused on machine readability than the original PowerShell DSC code. And since it's cross platform, it's possible to generate these files to be run on UNIX-style servers if you're working in a mixed environment. (And you can use native tools on those servers to generate
.MOF
files for Windows too if you wish)
Now we have our
.MOF
file, what do we do with it? Well DSC provides a new PowerShell CommandLet called
Start-DSCConfiguration
which is used to pass the
.MOF
files to the underlying management infrastructure and run them. In a test environment, you run something like:
Start-DSCConfiguration -Path .\EnableIIS -Verbose -Wait -Force
The
Path
parameter tells PowerShell where to look for
.MOF
files to process. The
Verbose
flag tells the runtime to display debug information so that we can see what's happening, and the
Wait
flag tells it to run synchronously so we can see what's happening. Finally the
Force
flag tells DSC that we're pushing a specific configuration, rather than expecting it to pull down appropriate configuration from a central server.
If we run that, we see something like this:
The debug output is saying that it's examined the server and discovered that IIS is already installed – so in this case it's done nothing.
So far, however, our script has been specific to a single server. That's nice for an example, but it's not very practical for the real world. You'd end up maintaining a separate script for each of the servers you manage and that would be a pain to keep everything up to date. Ideally we'd like to be able to write scripts which describe the state needed for a particular class of server (e.g. "Sitecore Content Deployment Webserver" or "SQL Server") and then just map those configurations to the appropriate server names for any given environment. How can we express our installation requirements in a more generic way to make them reusable?
It's actually pretty simple – we can pass configuration data into our DSC Scripts, and generalise the script to process the data. DSC expects you to pass in a dictionary object containing a tree of name-value pairs that describe your config data. For example, expanding on the IIS example above, we might pass in the following configuration data:
@{ AllNodes = @( @{ NodeName = "WebSvr001" Role = "IIS, SQL" TempFolder = "c:\dsc" InstallPath = "C:\Program Files\My Application" SQL = @{ InstallConfig = "SqlConfigurationFile.ini" SQLPassword = "p@55w0rd" } } ); }
It looks a bit cryptic, but it's just PowerShell's way of describing a tree of dictionary/array objects containing some data. (So it's basically PowerShell's equivalent of writing your config as JSON in a JavaScript application)
The
AllNodes
collection is an array of bits of data for individual Nodes in your DSC scripts. You can declare one entry in that collection per server that you want to configure. Each of these array elements needs the
NodeName
attribute (to give the name of the server this data is for) and the
Role
attribute to give the set of things which are going to be deployed to that machine. All of the rest of the data is up to you, and can be set up to meet whatever your needs are. For the purposes of an example here I've declared a couple of simple string properties for
TempFolder
and
InstallPath
and a dictionary of string properties for
SQL
. (I wrote a bit about
accessing these nested config variables
in a previous post)
You then need to modify your DSC Scripts to process the relevant bits of this data. The script for adding IIS doesn't actually need much of this information, but you might it to look like this:
Configuration EnableIIS { Node $AllNodes.where{ $_.Role.Contains(“IIS") }.NodeName { WindowsFeature WebServer { Name = "Web-Server" Ensure = "Present" } } } EnableIIS -ConfigurationData ".\configData.psd1"
There are two key changes here. The first is the declaration of the
Node
block. Instead of specifying the name of the server to apply this script to, it has changed to an expression. The code looks at all the elements in the
AllNodes
data we declared above, and returns the
NodeName
for any entry where the Role attribute contains "IIS". So our config data can describe a set of servers for databases, web servers etc. and this bit of DSC Script will only affect those servers which should have IIS installed.
The second change is that when we invoke our
Configuration
, we're now passing in a parameter called
ConfigurationData
that specifies the path to finding the config data above. This is how DSC knows where the config is when it processes your script to generate the
.MOF
files. Hence you can have multiple configuration files, and run a DSC script against each of them to (for example) generate
.MOF
files for development servers in different clusters.
As you'll see later in the series, you can also have custom parameters to pass in to
Configuration
s, in much the same way you can pass parameters in to CommandLets you've written in script.
Install all the things! My goal over the next few posts is to write up how we can use the basic concepts above to construct DSC scripts to take a Windows server from its basic post-install state to running Sitecore 8.x for development. I plan to cover the following aspects of that:
I'm not sure how many posts this will end up with, but I'll go through the assorted bits of script, and explain how they work for each of the key steps described above.
↑ Back to top