Moving from PowerShell DSC to Release Management vNext DSC

If you’ve learned plain PowerShell Desired State Configuration (DSC) and then move to Visual Studio Release Management vNext there are a lot of gotchas that can cost you many hours or event days to figure them out. I thought I share some things I’ve learned the hard way to get people up and running more quickly.

If you use plain DSC you normally have one structural configuration for the entire environment and an environment specific configuration that you change for each environment.

Environment Configuration
(Dev/Test/Prod)
$TargetFolder = “C:\inetpub\wwwroot\Demo”
$WebServerCount = 2
.psd1
Structural Configuration WindowsFeature IIS{
    Name = “Web Server”
    Ensure = “Present”
}
.ps1
Idempotent Automation foreach –parallel ($featureName in $Name){
    …
}
.psm1

The configuration for the environment is stored in a .psd1 file and you push the configuration from your deployment machine to all servers in the environment using remote PowerShell. A environment configuration (.psd1) could look like this:

@{
    AllNodes = @(
        @{
            NodeName    = "*"
            SourcePath  = "C:\Temp\cupcake"
            Destination = "C:\inetpub\wwwroot\cupcake"
        }

        @{
            NodeName    = "WebServer1"
            Role        = "Web"
        }

        @{
            NodeName    = "WebServer2"
            Role        = "Web"
        }
    );
}

The corresponding configuration would look like this:

Configuration SampleWebsite
{
    Node $AllNodes.Where{$_.Role -eq "Web"}.NodeName
    {
        # Deploy web content
        File WebContent
        {
            Ensure          = "Present"
            SourcePath      = $Node.SourcePath
            DestinationPath = $Node.Destination
            Recurse         = $true
            Type            = "Directory"
            DependsOn       = "[WindowsFeature]ASP"
        }
        
        ...
    }
}

SampleWebsite -ConfigurationData .\Configuration-DevEnv.psd1

Start-DscConfiguration -Path .\SampleWebsite -Wait -Verbose -Force 

If you now move to Visual Studio Release Management (RM) vNext the things are slightly different. In RM you have an additional point of configuration – the custom configuration of the action. As you can see in the picture the action has a property “ServerName”. So there is no need to configure the server names in a .psd1 file. It also can store custom configuration properties (default or encrypted). This is a big advantage if you have your scripts in source control and you need authentication data (like connection strings or passwords) for your environment.

image

So this leads to a complete different situation. Instead of having a structural configuration for the entire environment it’s better to have a configuration for each role and assign the server names in RM.

RM Action ServerName = WebServer1
ConnectionString = ********
Optional Configuration $TargetFolder = “C:\inetpub\wwwroot\Demo” .ps1
Structural Configuration
(Web Server, Database etc.)
WindowsFeature IIS{
    Name = “Web Server”
    Ensure = “Present”
}
.ps1
Idempotent Automation foreach –parallel ($featureName in $Name){
    …
}
.psm1

Note that I have marked the configuration as .ps1 instead of .psd1! I spent hours figuring this out – but RM will silently fail if you put the configuration in a .psd1 file. The configuration is just a script that runs before the normal configuration. You have to assign the Hashtable to a variable that you can use in the configuration. The properties from the RM action can be used as normal PowerShell variables. Since the computer name is set in RM and the configuration is pushed to each machine and executed there you can use $env:COMPUTERNAME instead of the real name. This way the configuration works on all machines and you can easily add a new server in RM without changing the configuration.

$ConfigurationData = @{
    AllNodes = @(
        @{
            NodeName         = "*"
            TargetFolder     = $DestinationPath
            ConnectionString = $ConnectionString
         }
        @{
            NodeName         = $env:COMPUTERNAME
         }
    )
}

The corresponding configuration would look like this. You do not have to start the configuration with Start-DscConfiguration. This is done by the RM engine.

Configuration WebServerConfiguration
{
   
    Node $AllNodes.NodeName 
    {
        File WebContent
        {
            Ensure          = "Present"
            SourcePath      = $applicationPath
            DestinationPath = $Node.TargetFolder
            Recurse         = $true
            Type            = "Directory"
        }

        ...
    }
}

WebServerConfiguration -ConfigurationData $ConfigurationData

So here a short list of the tips if you move your DSC configuration to RM:

  1. Do not create a configuration for an entire environment (Web server, data base, etc.). Create a configuration for each role in your environment instead.
  2. Do not use a configuration for each environment (Dev, Test, Prod etc.). Store environment specific data in the Release Management action and use one configuration for all environments.
  3. Store the configuration in a .ps1 file instead of a .psd1 file and assign the Hastable to a variable.
  4. Use $env:COMPUTERNAME so that you can reuse the configuration on any server.
  5. Create the configuration by executing it and provide the ConfigurationData parameter but do not execute it with Start-DscConfiguration. This will be done automatically.
  6. Make yourself familiar with the system variables of RM (see Donovan Browns post for a complete list). Especially the variable $applicationPath is nearly used in any of my configurations.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s