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.
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:
- Do not create a configuration for an entire environment (Web server, data base, etc.). Create a configuration for each role in your environment instead.
- 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.
- Store the configuration in a .ps1 file instead of a .psd1 file and assign the Hastable to a variable.
- Use $env:COMPUTERNAME so that you can reuse the configuration on any server.
- Create the configuration by executing it and provide the ConfigurationData parameter but do not execute it with Start-DscConfiguration. This will be done automatically.
- 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.