The DCS two step

When working with DSC a difficult concept can be that your desired state script is ‘compiled’ to a MOF file that is then ‘run’ by the desired state manager on the target machine. This is a two step affair and you have no real input on the second part. This is made more complex when Release Management is involved.

Colin Dembovsky did an excellent pair of posts on getting Release Management working with DSC based vNext templates. The core of the first post is that he made use of the Script DSC resource to run SQLPackage.EXE and MSDeploy to do the actual deployment of a system as well as using it to manage the transformation of the configuration files in his MSDeploy package.

This is where the two step issue raises it head. Even with his post I managed to get myself very confused.

The problem is Release Management passes variable into the DSC script and these are evaluated when the MOF is compiled. For most resources this is fine, but for the Script resource you have to be very aware that the string that is the actual script is treated as just a string, not code, so variables are not evaluated until the MOF is run by the desired state manager, by which point there are no Release Management variables set (they are long gone).

Colin’s provides an answer to this problem with his cScriptWithParams resource. This resource takes the Release Management provided properties and passes them into as parameters into the MOF compilation, forcing their evaluation, neatly side stepping the problem. He uses this  technique for the SetParameters.XML transform.

This is all good, but it got me thinking, his post has a number of hard coded paths, and also copies the deployment files to a ‘known location’. Is this really all required if we can pass in the Release Management $ApplicationPath?

So I swapped all my Script resources to use the cScriptWithParams resource passing in the applicationpath thus removing the need to copy the files from their default location.

      cScriptWithParams SetConStringDeployParam  
        {  
            GetScript = { @{ Name = "SetDeployParams" } }  
            TestScript = { $false }  
            SetScript = {  
                $paramFilePath = "$folder\_PublishedWebsitesWcfService\_PackageWcfService.SetParameters.xml"  
   
                $paramsToReplace = @{  
                      "\_\_DBContext\_\_" = $context  
                      "\_\_SiteName\_\_" = $siteName  
                }  
   
                $content = gc $paramFilePath  
                $paramsToReplace.GetEnumerator() | % {  
                    $content = $content.Replace($\_.Key, $\_.Value)  
                }  
                sc -Path $paramFilePath -Value $content  
            }  
            cParams =  
            @{  
                context = $context;  
                siteName = $siteName;  
                folder = $ApplicationPath;  

            }  
        }

 

          
        cScriptWithParams DeploySite  
        {  
            GetScript = { @{ Name = "DeploySite" } }  
            TestScript = { $false }  
            SetScript = {  
                & "$folder\_PublishedWebsitesWcfService\_PackageWcfService.deploy.cmd" /Y  
            }  
              
            cParams =  
            @{  
                folder = $ApplicationPath;  
            }

 

            DependsOn = "\[cScriptWithParams\]SetConStringDeployParam"  
              
        } 

I think this gave a easier to follow script, though I do wonder about my naming convention, maybe I need to adopt a nomenclature for inner script variables as opposed to global ones

Where are my parameters stored?

However, this does raise the question of where do these ‘global’ parameters come from? We have two options

  • A PowerShell Configuration data file (the standard DSC way)
  • Release management parameters

Either are valid, if you want to source control all  your configuration the first option is good. However what happens if you need to store secrets? In this case the ability to store a value encrypted within Release Management is useful.

In reality I expect will will use a combination. maybe with everything bar secrets in the configuration file.