A great practice to get into when building your PowerShell Desired State Configuration (DSC) infrastructure is separating DSC configuration data from the configuration itself because this improves the scalability of your DSC infrastructure.

Separating out static settings like specific registry key names, Windows feature names, hostnames, and so on from the actual configuration logic itself allows you to add to and change the configuration more quickly. To get a primer on this concept, refer to a previous article entitled Working with PowerShell DSC configuration data.

In this article, we're going to take this idea a step further and show you how to extend configuration data not just as a placeholder for node names but also to store all the configuration data necessary to generate a DSC configuration.

As explained in the previous article, a common use for configuration data is to store each node's name and some common attributes applicable to that node:

@{
 AllNodes = @(
     @{
         NodeName = 'SERVER1'
         FolderPath = 'C:\MyNewFolder'
     }
     @{
         NodeName = 'SERVER2'
         FolderPath = 'C:\MyOtherNewFolder'
     }
 )
}

The configuration above defines two nodes and a folder path that I will presumably want to be created on each of those nodes. I can continue to build out my configuration data in this manner, but at some point, I'm going to begin to duplicate code. I'll do this when I have multiple web servers, SQL servers, application servers, or really anything that requires a standard configuration across more than one server. I could just choose to make my configuration data look like this if I wish:

@{
 AllNodes = @(
     @{
         NodeName = 'SERVER1'
         FolderPath = 'C:\MyNewFolder'
         WindowsFeature = 'Web-Server'
     }
     @{
         NodeName = 'SERVER2'
         FolderPath = 'C:\MyOtherNewFolder'
         WindowsFeature = 'Web-Server'
     }
 )
}

But think about the situation where I've got hundreds or even thousands of servers. Now consider what happens if I want to associate some kind of other configuration item against all web servers. I'll have to do a lot of copying and pasting! There's a better way to do this, and the answer is to use the server role concept.

Grouping servers into roles allows you to define a standard set of configuration data and simply "point" each node to that grouping. When making changes, you can then simply update a single item, and it will apply to all nodes of the same role type. Let's see how we can implement this role concept for DSC configuration data.

In a DSC configuration data hash table, the common key that's added is the AllNodes array, which defines each particular node and settings only applicable to each. However, you can also add a key called NonNodeData, which is not node-specific and can apply across any number of nodes.

Let's take our previous web server example from above and, instead of setting a Windows feature on each node, we'll define a WebServer role and set this feature in there. Then, afterward, we can "point" each node to that group. To do this, I'll need to add the NonNodeData key to the configuration data and change each node to associate that role with that group:

@{
 AllNodes = @(
     @{
         NodeName = 'SERVER1'
         FolderPath = 'C:\MyNewFolder'
         Role = 'WebServer'
     }
     @{
         NodeName = 'SERVER2'
         FolderPath = 'C:\MyOtherNewFolder'
         Role = 'WebServer'
     }
 )
 NonNodeData = @{
     Roles = @{
         'WebServer' = @{
             'WindowsFeatures' = @(
                 'WebServer'
             )
         }
     }
 }
}

If I want to refer to this hash table in PowerShell, you'll see that I can read the nodes via dot-notation as expected.

Configuration data hashtable

Configuration data hashtable

I can then enumerate each node that I've "tagged" as having a WebServer role as well.

Enumerating DSC nodes

Enumerating DSC nodes

I now have the ability to add this logic into a DSC configuration.

Configuration WebServer

{
 Node $AllNodes.NodeName  {
 $featuresToInstall = $ConfigurationData.NonNodeData.Roles.($Node.Role).WindowsFeatures
 foreach ($feature in $featuresToInstall) {
     WindowsFeature $feature {
         Ensure = 'Present'
         Name = $feature
     }
 }
     }
}

I'll run the configuration, and you'll see that it should have generated a MOF file for each node defined in configuration data.

Subscribe to 4sysops newsletter!

WebServer -ConfigurationData $configData
MOF file

MOF file

You are now in a position to add as many roles and configuration items as you need. Just add another role to NonNodeData and another group, as I have here with WindowsFeatures. This allows you to add, remove, and replace all kinds of configuration data at will without having to duplicate code!

10 Comments
  1. Arie H 5 years ago

    Its a nice way to use the NonNodeData section.

    I usualy use the NodeName = '*' at the top to add the common base (like WindowsFeatures) for all nodes that are mapped to this role.

    Do you consider the method you show here of one configuration file for more then one role as a good practice or using multiple configuration files, one per role and then using the NodeName='*' to store base configuration to be better ?

    Personally I prefer and endorse the usage of  NodeName='*' but I guess its a matter of the amount of servers you have and of which role each belongs to.

     

    Thanks for the read !

     

    Arie H

    • Author

      That would work as well but you don't really have the control if you use that method. I like storing config data in a single file and having a single configuration script that reads it. But, as you say, there are lots of different ways to get the same thing done.

  2. Jim 4 years ago

    Every example I have found has each node referenced individually in the configuration data section. What I'm trying to do is create one configuration data section, and feed it names of multiple nodes to have the configuration data section applied to. I'm wondering if putting the configuration data section in a foreach would serve that purpose...

  3. Author

    You will have to somehow have the names referenced individually in the configuration data section. However, that's not to say they couldn't be added dynamically. You can create a step to insert text into a text file that includes the host name.

    • Jim 4 years ago

      What confuses me a bit in trying to create a SQL DSC script using xsqlserversetup, is seeing different  examples of scripts using parameters in the configuration data section in the same way as named parameters on the command line, to feed the xsqlserversetup section. Some examples are extremely minimalist, containing only the node = , and psdscallowplaintextpassword = settings, and others full of various parameter settings. I know there is a lot of freedom and flexibility in powershell, but to your knowledge, is there any discussion of "best practices" or preference as to how the configuration data section should be best used?

      • Author

        The configuration data section is simply a hashtable. It can be whatever you want. Normally though, you have one embedded hashtable with is a NodeName of * with common settings then you've got a listing of all of the nodes after that. Unfortunately, the name has to be in there so if you don't want to hardcode them in there you could create a script to create a ConfigurationData hashtable on the fly and pass to the -ConfigurationData parameter.

  4. jim 4 years ago

    I got a DSC script working which uses the xsqlserversetup resource and configuration data. What I was still wondering is if there were any pros or cons of putting variables in one of those sections vs the other?

  5. sivaji 4 years ago

    my info.psd1 file contains:
    @{

    AllNodes = @(

    @{
    NodeName = "*"
    },
    @{
    NodeName = "DSC-Client"
    Role = "web"
    },
    @{
    NodeName = "DSC-SQL2014"
    Role = "sql"
    }
    )
    }

    my node configuration is:

    Configuration MyWebApp
    {
    Import-DscResource -Module PSDesiredStateConfiguration

    Node $AllNodes.Where{$_.Role -Contains "web"}.NodeName
    {
    File CreateFile {
    Ensure = "Present"
    DestinationPath = "C:\Users\devopsuser1\Documents\Test.txt"
    Contents = "Hello DSC"
    }
    }

    Node $AllNodes.Where{$_.Role -Contains "sql"}.NodeName
    {
    File CreateFile {
    Ensure = "Present"
    DestinationPath = "C:\Users\devopsuser1\Documents\Test.txt"
    Contents = "Hello DSC"
    }
    }
    }
    MyWebApp -ConfigurationData "C:\Users\devopsuser1\Desktop\info.psd1"

    when i run this this configuration iam getting two mof files with "DSC-Client.mof" and "DSC-SQL2014.mof".
    Now i have to run LCM for that can i generate guid and checksum for each mof file and placed in the path "C:\Program Files\WindowsPowerShell\DscService\Configuration" and update my LCM with latest GUIDS for 2 mof files is necessary.

    Plz suggest on this issue.

    Thanks
    sivaji

  6. Nick 8 months ago

    What if a node has multiple Roles?  This doesn't seem to work with that scenario

Leave a reply

Please enclose code in pre tags

Your email address will not be published. Required fields are marked *

*

© 4sysops 2006 - 2021

CONTACT US

Please ask IT administration questions in the forums. Any other messages are welcome.

Sending

Log in with your credentials

or    

Forgot your details?

Create Account