The DscConfiguration module is one of the tooling modules originally written by Steven Murawski for Stack Exchange. It has since been released as open source, and the main repository for the code is hosted by PowerShell.org.
The DscConfiguration module provides us with two main pieces of functionality. First, it allows us to store the ConfigurationData hashtable in several smaller, more manageable data files. This is a vast improvement over the experience you would have if you had to maintain the entire ConfigurationData database in a large, single file. Once you scale out beyond a few machines, you quickly wind up with a file that is thousands of lines long. It can be easy to overlook errors in such a large file.
The other major piece of functionality provided by the DscConfiguration module is the introduction of the concept of Sites, Services, and Global settings, in addition to Nodes. Instead of needing to define every setting directly on each Node – which could introduce a large amount of duplication throughout the ConfigurationData table – you can define settings that are inherited by all nodes, by nodes in a specific site, or by arbitrary groups of similar nodes (which are referred to as Services in the DscConfiguration module.)
When we began working with the Stack Exchange modules, the DscConfiguration module’s code had the most complexity. As Steven described it, the module had grown organically over time, in response to changing needs within Stack Exchange. We have collaborated with Steven to simplify the code in this module, making it easier to understand and to document, in some places.
Here are some of the changes we’ve made to the module at DevOpsGuys:
We moved the Resolve-DscConfigurationProperty function from the DscBuild module to the DscConfiguration module.
It makes more sense for it to live here, with all of the rest of the code that has explicit knowledge of how the ConfigurationData hashtable has been structured.We speculated that this function was originally in the DscBuild module due to a block of code which would look up the value of the $ConfigurationData variable if it were not explicitly passed into the function. Refer to this gist for the original code an our updates.
Presumably, the intention of that code was to allow you to call Resolve-DscConfigurationProperty from a configuration script without having to bother to explicitly pass along a value to the -ConfigurationData parameter on every call. The problem with the original implementation is that it used the Get-Variable cmdlet, which will only resolve variables in the same script module as its caller. If you were using Invoke-DscBuild, you wouldn’t notice the difference, because that command also contains a $ConfigurationData variable that would eventually be resolved. If you tried to call a configuration from outside of Invoke-DscBuild, though, the calls to Resolve-DscConfigurationProperty would fail (unless you passed in -ConfigurationData explicitly.)
By converting this block to use $PSCmdlet.GetVariableValue() instead, the function can now be called from a configuration even if you’re not using the Invoke-DscBuild command from the DscBuild module, and without explicitly using the -ConfigurationData parameter.
We took out the concept of “Applications”, replacing it with the ability to define a hierarchy of properties.
In the original Resolve-DscConfigurationProperty function, there were two ways you could use it: you could pass in a -PropertyName string, or an -Application string. If you used -Application, the function would look up values in a slightly different place, and would return a hashtable containing whatever values were needed to install a particular application (Name, ProductId, SourcePath, Installer, etc.) The code itself was quite complex, due to having checks for whether $PropertyName or $Application was being used in virtually every helper function.
We speculated that the reason for this Application parameter’s existence was because Stack Exchange needed a way to return a container for other properties, instead of just a single flat PropertyName. In order to simplify the code and make it more flexible for users, we extended this concept to all properties. You can now pass in a value for PropertyName which looks like a file system path, and it will resolve those values by looking through nested hashtables. Refer to this gist for examples of how this looks for the caller, before and after our changes. One advantage of this new approach is that you can override individual values of the nested hashtables without duplication; with the old Application implementation, you would have to repeat the entire table even if you only wanted to override one key / value pair.