Developing DSC scripts for the Azure Resource Manager DSC Extension

PowerShell Desired State Configuration (DSC) has been out for a while and there are lots of folks that really get it.  Azure Resource Manager (ARM) is going strong, and brings along with it a new contingent of software professionals that can leverage DSC in their ARM templates through the use of the DSC VM Extension.

This post assumes you know how to build and execute an ARM template using Visual Studio 2015 and PowerShell.  It also leverages a set of DSC resources in the PowerShell Gallery.  New capabilities are coming to ARM rapidly.  If you’re reading this more than a couple of months after 9/1/2015, things have probably changed.  Comment back to the blog and request an update if you notice something out of sync.

Background

With DSC, the desired outcome, described in a simple declarative script, is applied to the machine or machines specified.  DSC goal seeks to ensure that the outcomes described in the script are reflected in the final state of each machine.  These outcomes could include Windows Features, firewall rules or other network settings, Active Directory creation, SQL Server AlwaysOn clusters, and so on.  Just about anything that you can do with PowerShell.  Take a look in the PowerShell Gallery to see the kinds of things you can do.

ARM is an Infrastructure as Code (IaC) tool.  Its job is to allow the engineer to describe the environment and assets in that environment, then goal seek to ensure those outcomes are achieved.  So, networks, VMs, NICs, storage accounts, and so on are all built according to the ARM template. 

ARM + DSC allows you to both build the environment and configure the VMs.  In this post, I’ll bootstrap the DSC development techniques necessary to start building your own DSC scripts and use them with ARM.

Implementation

Since this isn’t really about ARM templates, I’ll just start with a working ARM template that builds “just a vm”:  JustAvm.  To this VM, I’ll add a firewall rule using a DSC script.  The repo contains a VS 2015 solution.  If you want, pull out the PowerShell script (Deploy-AzureResourceGroup.ps1), the template (DeploymentTemplate.json) and the parameter file (Deployment Template.param.dev.json) rather than grabbing the whole solution. 

At a high level, the process looks like this:

  1. Run the template to build the empty VM.
    (the initial commit of the repo holds only the VM bits.)
  2. RDP to the VM, disable IE Enhanced Security (just to make it easier to access web resources while in development)
  3. Download and install Windows Blue.  Reboot.
    (this step is done automatically when you use the ARM DSC Extension)
  4. At the command prompt, Install-Module –Name xNetworking
    (You’ll get a couple of prompts, answer Yes.  This is where you’re grabbing a module from the PowerShell gallery mentioned above.)
  5. Define your configuration and test function
    (Details below)
  6. Execute your test function and verify results.
    (Details below)
  7. “Rinse and repeat” until your configuration is complete.  Steps 5, 6, 7.
  8. Copy the configuration to your laptop.
  9. Publish the Azure VM DSC Configuration.  Copy the output zip to someplace reachable.
    (Details below)
  10. Insert DSC Extension resource into your template.
    (to get the updates to the template that include the extension, grab the 2nd commit from the repo.)
  11. Test.  Assuming your DSC script is correct, this involves deleting the resource group created in step 1 then running the template again, but this time with your DSC Extension bits within.  If you want, you can delete just the VM out of the RG before testing.

Step 5 Details

Start up PowerShell ISE as Admin.  Copy the test script (below) into the Script Pane.  Highlight all & F8 (run selection). 

Here’s the DSC script + test function:

configuration InstallFWRule
{
Import-DscResource –ModuleName ’PSDesiredStateConfiguration'
Import-DscResource -ModuleName xNetworking

Node $AllNodes.NodeName
{
xFirewall DatabaseEngineFirewallRule
{
Direction = "Inbound"
Name = "SQL-Server-Database-Engine-TCP-In"
DisplayName = "SQL Server Database Engine (TCP-In)"
Description = "SQL Server Database Engine: allow inbound TCP traffic."
DisplayGroup = "SQL Server"
State = "Enabled"
Access = "Allow"
Protocol = "TCP"
LocalPort = 1433
Ensure = "Present"
}
LocalConfigurationManager
{
RebootNodeIfNeeded = $true
}
}
}
function Start-InstallFWRule
{
$ConfigData = @{
AllNodes = @(
@{
# The name of the node or nodes we are describing
NodeName = 'localhost'
};
);
}
Write-Verbose "Generate DSC Configuration..."
InstallFWRule -ConfigurationData $ConfigData -OutputPath .\InstallFWRule

Write-Verbose "Starting Configuration..."
Start-DscConfiguration .\InstallFWRule -wait -Verbose -Force
}

Step 6 Details

At the command prompt, enter “Start-InstallFWRule”.  This will run the configuration against the local computer.  You should see output confirming the actions taken.  You can also check in the Windows Firewall tool to confirm outcomes.

Step 8 Details

In my Temp directory, I create a folder for this next step.  Inside it, I store the configuration that I just developed.  The test function should not be included.

DSCSample

Step 9 Details

At a PowerShell command prompt, navigate to your new folder.  Execute the following:  (creates the zip file)

  • Switch-AzureMode –Name AzureServiceManagement
  • Publish-AzureVMDscConfiguration
    –ConfigurationPath .\InstallFWRule.ps1
    –ConfigurationArchivePath .\InstallFWRule.ps1.zip

Step 10 Details

Add a VM extension resource to your ARM template.  (Carriage returns added for formatting.)

“variables”:{

"compute_EXT_vm_Extension_Name": "[concat(
variables('compute_VM_vm_Name'),
'/InstallFWRule')]",
"compute_EXT_vm_ModulesURL":
"[concat('https://', variables('storage_Acct_1_Name'),
'.blob.core.windows.net/public/InstallFWRule.ps1.zip')]",
"compute_EXT_vm_ConfigFunction": "InstallFWRule.ps1\\InstallFWRule"
}

“resources”: [

{
"type": "Microsoft.Compute/virtualMachines/extensions",
"name": "[variables('compute_VM_dc2_Extension_Name')]",
"apiVersion": "2015-06-15",
"location": "[resourceGroup().location]",
"dependsOn":[
"[variables('compute_VM_vm_Id')]"
],
"properties": {
"publisher": "Microsoft.Powershell",
"type": "DSC",
"typeHandlerVersion": "2.1",
"settings": {
"modulesUrl": "[variables('compute_VM_vm_ModulesURL')]",
"configurationFunction": "[variables('compute_VM_vm_ConfigFunction')]"
}
}
}]

Follow Through

How can you tell that it worked?  How can you debug if it didn’t work? 

If the deployment is successful, the output in the PowerShell session will not have any red text.  And you can check inside the VM for the outcomes that you expected using RDP or other means that fit your circumstances.  Another very useful tool is Azure Resource Explorer.  It is located at https://resources.azure.com.  After authenticating with the same credentials that you use to enter the vNext portal, you can drill down into your subscription, resource groups, etc.  Look at the “InstanceView” of your vm.  (Hint: to view the output more easily, copy the block of text with embedded \r\n to Notepad++.  Do an extended change of “\\n” to “\n”.)

ResourcesAzureCom

The first step in debugging is to look at the Deployment output in the vNext (aka Ibiza) portal.  Failed steps will be pink in color and will have detailed output.  This usually gives sufficient hints to take debugging steps.  If it’s too arcane to be helpful, the next step is to RDP to the VM.  Also, in the case of a failure, InstanceView may not be populated in the Azure Resource Manager.  The JustAvm template hooks up the RDP endpoint for this reason. 

Explore the logs in these locations:

  1. C:\WindowsAzure\Logs\AggregateStatus
  2. C:\WindowsAzure\Logs\Plugins\Microsoft.Powershell.DSC\2.1.1.0

Confirm installation of your PowerShell DSC Modules here:

  1. C:\Program Files\WindowsPowerShell\Modules

Cheers!