DSC Dynamic Resource Parameters

Apologies if this comes as a double post. I tried posting it on Friday and I don’t see it in my profile or on the forum. Since it’s been well beyond 48 hours (unless it’s 48 hours during business days), I thought I’d give it another shot.

I’m an intermediate PowerShell user who’s just starting into Desired State Configuration. I am attempting to write a script that will take the value of a Role Property in each Node, compare it to a list of Roles passed in the NonNodeData, and use the values listed there to configure WindowsFeatures and Files.

A shorter, sample version of the PSD1 I’m working with is below:

    #Node Specific Data
    AllNodes = @(
            #Information that applies to the configurations of all Nodes below
            NodeName = '*'

            NodeName = "ServerName1"
            Role = "Front-End"
    ); #end AllNodes Array

    NonNodeData = @(
            Name = "*"
            Value = @(
                @{Name="Telnet-Client"; Type="WindowsFeature"; Ensure="Absent"; IncludeAllSubFeature="False"; LogPath="C:\Temp\DSC-WindowsFeature.log"; DependsOn=""; Source=""},
                @{Name="Utility"; Type="File"; Ensure="Present"; DestinationPath="D:\Utility"};
            ); #Value
        } #*

            Name = "Front-End"
            Value = @(
                @{Name="Web-DAV-Publishing"; Type="WindowsFeature"; Ensure="Present"; IncludeAllSubFeature="False"; LogPath="C:\Temp\DSC-WindowsFeature.log"; DependsOn="[File]Utility"; Source=""},
                @{Name="XPS-Viewer"; Type="WindowsFeature"; Ensure="Present"; IncludeAllSubFeature="False"; LogPath="C:\Temp\DSC-WindowsFeature.log"; DependsOn=""; Source=""}
            ); #Value
        } #Front-End

    ); #end NonNodeData Array
} #end ConfigurationData

You can see that in a given role, there can be multiple WindowsFeatures that need to be configured with different parameters. For example, “Web-DAV-Publishing” has a DependsOn value, while “XPS-Viewer” does not.

In order to get around this, I tried the following within a foreach that loops through the Value array:
(The $resource variable contains the XML element for a single Resource Hashtable in the Value array of NonNodeData. Empty or null keys have been removed.)

		WindowsFeature $resource.Name
                    Name = $resource.Name
                    if($resource.ensure){Ensure = $resource.Ensure}
                    if($resource.IncludeAllSubFeature -eq $True){IncludeAllSubFeature}
                    if($resource.LogPath){LogPath = $resource.LogPath}
                    if($resource.DependsOn){DependsOn = $resource.DependsOn}
                    if($resouece.Source){Source = $resource.Source}

In theory, this would create a WindowsFeature block with only the Parameters present in the hashtable for that resource. In reality, only the Parameter names are valid within the Resource block, so it errors out.

I haven’t had any luck in my attempts to dynamically include or exclude parameters based on the hashtable. I could create a block for each possible combination of properties, but that feels a bit ridiculous (and tedious, and time-consuming). There has to be a better way. Or not, if I’m running into a limitation of DSC, or not thinking about it the way I should.

Any help or guidance on this would be much appreciated. I’d like to use DSC for this, but if I can’t find some resolution to this issue I’ll have to scrap it and go the old-fashioned route.

Keep in mind that a configuration gets compiled into a MOF, which is a static text file. The scheme for each resource is also a MOF - a static text file. And, that scheme must represent the entirety of what the resource is and does. That’s why “dynamic parameters” aren’t a thing there - there’s no way for a static, text-based scheme (at least, not one as simplistic as MOF) to represent something that changes from moment to moment.

So what you’re running into is a design constraint of MOF being the ultimate destination for all this, and of the Configuration language reflecting that constraint.

So. What could you do?

I’d probably look at making the Configuration less dynamic, and instead adding the logic elsewhere. For example, a composite resource could be used. Make one for each possible role, and have each one reflect the sub-configurations that each role requires. Another option - and this would be AWESOME - would be to make a custom resource which is given a role name, and which looks up in some central database the things associated with that role, and which then makes those happen on the node itself.

Adding this kind of intelligence is what the Tug pull server project is, in fact, all about. If you were using that, then your configurations could be produced dynamically and on-demand as the node requested them, perhaps using a back-end database to look up the configuration artifacts each role demanded. The idea with Tug is that it abstracts the HTTP(S) part of the pull server, and the pull server protocol, into, essentially, a bunch of back-end PowerShell commands that you write. So you could write commands to implement any pull server-side functionality you wanted (assuming, of course, you’re using Pull mode.)

So there’re options for you. It just typically involves a somewhat dumber configuration, and a somewhat more modular approach to it all.