Jeremy Davis
Jeremy Davis
Sitecore, C# and web development
Article printed from: https://blog.jermdavis.dev/posts/2015/config-variables-in-powershell-dsc-can-confuse

Config variables in PowerShell DSC can confuse

Published 28 September 2015
Updated 26 June 2018

I'm in the middle of preparing a talk for an upcoming Sitecore user group on the subject of using PowerShell DSC for Sitecore installs. (I'll post a write up of the talk after I've given it) And one of the things I've discovered is that once you get into the guts of it, using configuration variables alongside Script Resources can be a bit confusing.

So, for the benefit of my future self (I'm bound to forget this as I am taking time off from my preparation to head over to the US for this** and this** ) I'm writing down what I've worked out...

Once you move away from very basic configurations, it seems to me I'll end up with nested dictionaries of Name/Value pairs in the configuration for my DSC scripts. I found myself with something along the lines of:

@{
    AllNodes = @(
        @{
            NodeName = "My-Server"
            Role = "RoleOne, RoleTwo"
            
            RoleOneSoftware = @{
                DiskPath = "c:\SomeFolder\"
                SettingOne = "alpha"
                SettingTwo = "bravo"
            }

            RoleTwoSoftware = @{
                DiskPath = "c:\OtherFolder\"
                SettingOne = "charlie"
                SettingTwo = "delta"
            }
         }
    );
}

					

Contrived example – but the idea is that in order to keep the setting names sensible, you probably end up nesting things. Easy enough to declare, and better (I think) from the perspective of managing your config settings in the future.

But what happens when you try to make use of these values?

Well for most of DSC's Resources, it's not too bad. Inside your Node block, there's a variable defined for you called $Node that contains the data above for the node which has been selected by the script when it's executed. And it's a dictionary object that works a bit like a "dynamic" variable in C# so you can just reference the properties:

Configuration ContrivedExample
{
    Node $AllNodes.where{ $_.Role.Contains("RoleOne") }.NodeName
    {
        File CreateTheRoleFolder
        {
            Type = "Directory"
            DestinationPath = "$($Node.RoleOneSoftware.SomeFolder)"
            Ensure = "Present"
        }
    }
}

					

The enclosing $() force PowerShell to treat what's inside as a variable name, in order to prevent any confusion on its part. I don't think this is always necessary – but in my head at least it's easier to use it everywhere than occasionally be confused when a particular Resource fails because you forgot to use it when you needed to.

Things get a bit more complicated when the resource in question is a Script resource though. A side-effect of how these are implemented is that when you're filling in the GetScript, TestScript and SetScript properties, you're specifying strings not scripts. So you need to be very careful about how you specify variables because the usual rules of how PowerShell does variable replacement don't quite work with your configuration data.

There are two things to remember. By default, variables you write in the normal $someName way are going to be evaluated at the point that your script code is modifying a server. That's not the point where you run the Configuration you're declaring here. That means you can't reference variables declared by PowerShell DSC in this way (for example $Node) because their values are not available when the script runs. For those variables, there's a special syntax. You have to write $using:Node instead. This causes PowerShell DSC to inject the value correctly into the output when it "compiles" your DSC script into the file which Windows uses to change the state of your computer.

The trouble is this starts to look very messy when you're dealing with configuration variables, and I had a lot of trouble getting it to work for a while. Where I've ended up is that I've found myself declaring a lot of temporary variables, to make the code a bit clearer to me. For example:

Configuration ContrivedExample
{
    Node $AllNodes.where{ $_.Role.Contains("RoleOne") }.NodeName
    {
        Script DemonstrateVariables
        {
            GetScript = {
               # whatever code goes here
            }
            TestScript = {
               # whatever code goes here
            }
            SetScript = {
               $diskPath = $using:Node.RoleOneSoftware.DiskPath
               $settingOne = $using:Node.RoleOneSoftware.SettingOne

               $computedValue = "$diskPath\$settingOne"

               Write-Verbose "The computed value is $computedValue"
            }
        }
    }
}

					

It's not pretty – but using that approach has worked for me.

Hopefully as my DSC knowledge increases I'll work out how to make better use of this...

Expired links

** Some links in this page have expired. The originals are listed here, but they may no longer point to the correct content:
  1. this
  2. this
↑ Back to top