Another common challenge with DSC, is how to compose DSC configurations.
People have seen the trick of having a Configuration, and the following code within:
This is a good way to get started and works well for small-ish configurations, but it gets out of hand pretty quickly, as it’s hard to read all the
if statements and their content. Some variant of this are using
Where clauses around the Node expression.
Puppet and the Role and Profiles model
The puppet community uses and recommend a very neat model for composing complete system configurations, while managing the complexity by modularising parts of the code into re-usable and cohesive components, while loosely coupling them.
They also have comprehensive and well-written documentation with real-life examples available on their website, making it easy to learn.
This article is a quick explanation of the principle and how to do something very similar with DSC.
The DSC Composition Model
I find the names of the DSC code constructs potentially confusing for people not already familiar with their specificities and how to compose them into system configurations.
The DSC code constructs are:
As you see, to avoid confusion, I like to prefix their name with ‘DSC’ when talking about the code constructs, in order to separate them clearly from my attempt of vulgarising the composition model for DSC:
- Configuration Data [data]
- Configurations [DSC Configuration + DSC Composite Resources]
- Resources [DSC Resources + DSC Composite Resources]
Yep, until proven otherwise, I don’t find DSC Composite Configurations useful.
I’ve already discussed the DSC Configuration Data Problem and how it should be structured at a high level, so I’ll completely ignore my advice, and Keep It Super Simple to focus on the Composition Model.
I see the composition model as a succession of abstraction layers:
the top layer, the Configuration Data Layer is ‘tool agnostic’ (it’s just structured data), and the most high-level. This is where changes happen more often, and should be very declarative and self documenting, representing the business context for the entities it describes (i.e.
Nodes, but not exclusively). You can call it the Policies, because the Data is structured into documents that describe what the entity/object it represents should look like. DSC is the platform to converge the entities into compliance.
the middle layer is the Configuration layer, where the data is adapted, transformed and massaged slighlty from business-specific structure into something that makes sense for the Resources, with just a touch of orchestration (think dependsOn and waitfor*…). The configuration usually build a ‘layered technology stack’, in a cohesive unit that represents an entity. For instance you could compose NIC, System Disk, OS, Domain, Accounts to represent a basic system.
At the bottom, the Resource layer is the interface with a specific technology, where atomic changes are made. It should have very little logic handling the overall goal, but transpose the DSL into actionable and idempotent actions. It usually is the DSC interface to imperative modules, whether they’re PowerShell modules, or other such as Python or C DSC resources for Linux.
The common mistakes I’ve seen, is over-specializing the middle or lower layer, usually in response to the challenge posed with managing Configuration Data.
This surfaces when using too many DSC Script Resources (instead of custom DSC Resources), or having big monolithic configurations with lots of logic, and unhelpful parameters (i.e. passing the whole
From these layers, how can we compose Configurations in a flexible way, that can be self-documenting, flexible, with a reduced change domain (aka change scope)?
One Role, multiple Configurations
If we think about some kind of application running on a server, you can easily spot a layered stack. Well, I just described it, you have the ‘Server’ and the ‘Application’.
Now, we may want to run that application on two different types of server, so we’d have ServerType1 and ServerType2 both running an instance of Application.
That gives us 2 unique compositions:
Creating a unique DSC Composite Resource full of if statement to manage this could work, but it would be painful to use when you only want to change
ServerType2 are relatively similar, maybe only the configuration data changes (such as Disk Size, OS Version, NIC Configuration), and only one DSC Composite Resource is required for
ServerType, on top of the
Application DSC Composite Resource.
One way we could interpret the
ServerType1_application composition could be:
In Puppet’s Role and Profiles model, that’s a Role definition, including two profiles. For DSC I’d call it the Roles and Configurations model.
We can now imagine that we associate several
Nodes with this role, and we can start raising cattles! The nodes kinda ‘instantiate’ the Roles.
Assuming a Configuration Data structure like the one below, we have simple Nodes implementing Unique roles, composed of re-usable Configurations but with Data specific to the role.
The Configurations (DSC Composite Resources) would be re-usable, and probably live in different PowerShell modules (maybe one for the Platform and one for the Application or Product).
Some Data would be duplicated here (i.e. the Application Data for each Role), but that’s a subject for another time (Datum).
Now the question is how do we make the magic happen between this Configuration Data, and the Configurations?
Splatting things together
I’ve blogged about Pseudo-Splatting DSC resources, and this is the same principle. DSC Composite Resources behave in a similar way as DSC Resources when compiling MOFs, so we can splat the Parameters defined in our Roles to the respective Resource:
The powershell pseudo code equivalent would be:
Should we define the function to ‘splat’ the DSC Composite Resouce like so (available in Datum):
The actual DSC code would look like this:
As you’ve seen this is an approach to managing DSC Configuration, that has been proved and tested with other Configuration Management platforms. Chef, Puppet, Ansible all have implemented similar patterns.
This is also what I use for my DscInfraSample project, check it out (I know it needs some love… I’ll get there)!