PowerShell Desired State Configuration (DSC) is a new Powershell feature shipped with Windows Server 2012 R2 and Windows 8.1. In short, it allows you to specify what a machine configuration should look like and leaves it to the so-called Local Configuration Manager on the target machine to get into this desired state. There are plenty of resources available if you’d like to learn more so I won’t get into Powershell DSC details here. I can particularly advise the Powershell DSC Book from PowerShell.org.

I was configuring an Azure Virtual Network (VNet) through PowerShell as part of a larger script and thought it would be a nice exercise to write a basic PowerShell DSC resource for configuring the network. PowerShell options for configuring an Azure Virtual Network are quite limited: the most important cmdlet is Set-AzureVnetConfig that accepts an XML file that must describe your entire network configuration. There are no cmdlets for adding a virtual network site or DNS server, for example. This is somewhat limiting but it makes the initial job of writing a DSC resource a lot easier.

First, you should download the latest PowerShell DSC Resource Kit (at the time of writing this is Wave 5). It contains two things we need among a lot of other DSC resources: the xAzure module containing the xAzureSubscription DSC resource for setting up an Azure subscription before configuring our virtual network and the xDscResourceDesigner module that allows easy creation of DSC resources. Unzip the resource kit to C:\Program Files\WindowsPowerShell\Modules like this:

DSC Resource Kit Install Directory

The DSC resource we’re creating mimics the behavior of the Set-AzureVnetConfig in that it only accepts a path to the .netcfg file. Besides we add one additional property for later use: the current XML configuration. The script to create the DSC resource template is as follows (run as administrator):

Import-Module xDSCResourceDesigner

$ConfigurationPath =
    New-xDscResourceProperty -Name ConfigurationPath -Type String -Attribute Key
$CurrentVNetConfig =
    New-xDscResourceProperty -Name CurrentVNetConfig -Type String -Attribute Write

New-xDscResource -Name cAzureVNetConfig `
                 -Property $ConfigurationPath,$CurrentVNetConfig `
                 -Path 'C:\Program Files\WindowsPowerShell\Modules' `
                 -ModuleName cAzure -FriendlyName cAzureVNetConfig -Force

Running this script results in the following files and folders in C:\Program Files\WindowsPowerShell\Modules:

C:\...\cAzure\cAzure.psd1
             \DSCResources\cAzureVNetConfig\cAzureVNetConfig.psm1
                                           \cAzureVNetConfig.schema.mof

The cAzure.psd1 file is the module manifest. The cAzureVNetConfig.schema.mof file is the WMI Managed Object Format file that describes our resource to WMI. And finally cAzureVNetConfig.psm1 that contains the actual scripts for the DSC resource. It consists of three functions:

  • Get-TargetResource: this function usually retrieves the current state of the target system. For example, the built-in File DSC resource may check to see whether a specific file or directory exists and return this information.
  • Set-TargetResource: this function is responsible for manipulating the target system so that it gets into desired state.
  • Test-TargetResource: this function should return $true or $false, indicating whether the target system is in the desired state or not.

Let’s start with the simplest: Test-TargetResource. For now, we just let it return $false, indicating that the Azure Virtual Network is not in the desired state. We could implement a comparison between the XML document we provide and the current configuration to check for differences but all that would gain us is preventing an unnecessary Azure network configuration update. So Test-TargetResource is as follows:

function Test-TargetResource
{
    [CmdletBinding()]
    [OutputType([System.Boolean])]
    param
    (
        [parameter(Mandatory = $true)]
        [System.String]$ConfigurationPath,

        [parameter()]
        [System.String]$CurrentVNetConfig
    )
    $result = $false
    $result
}

Next is Get-TargetResource. It’s main job is to get the current network configuration, although we don’t do anything with it in this first version:

function Get-TargetResource
{
    [CmdletBinding()]
    [OutputType([System.Collections.Hashtable])]
    param
    (
        [parameter(Mandatory = $true)]
        [System.String]$ConfigurationPath
    )

    #Get current VNet config.
    $currentVNetConfig = (Get-AzureVNetConfig).XMLConfiguration

    #Return both configuration path and the current configuration.
    $returnValue = @{
        ConfigurationPath = $ConfigurationPath
        CurrentVNetConfig = $currentVNetConfig
    }
    $returnValue
}

The function takes one parameter: the configuration path for our new network configuration and it returns a hash table with the configuration path and the current configuration.

The last function we need is Set-TargetResource which applies our new network configuration:

function Set-TargetResource
{
    [CmdletBinding()]
    param
    (
        [parameter(Mandatory = $true)]
        [System.String]$ConfigurationPath,

        [parameter()]
        [System.String]$CurrentVNetConfig
    )

    #Set the new VNet config from the specified configuration path.
    $operation = Set-AzureVNetConfig -ConfigurationPath $ConfigurationPath -Verbose -Debug
    Write-Verbose "Setting Azure VNet config result: $operation.OperationStatus"
}

It takes two parameters, the configuration path and the current configuration and then calls Set-AzureVNetConfig using the specified configuration path. Pretty easy, all combined. Now on to how to use all of this. And a tip: before using our new resource, despite it being in the correct folder (on PSModulePath), you may need to reboot. I have no idea why.

To use the new resource you have to write a configuration, use the configuration to generate a MOF file for our target system and apply the MOF file. The target system in this case is localhost since we run the configuration from the current machine. To be able to retrieve the current Azure VNet config you need an Azure publishsettings file. You can download yours here: https://windows.azure.com/download/publishprofile.aspx. Once you have your publishsettings file and a valid Azure VNet config, you can run the following script to run our newly created resource.

Configuration SetAzureVNet
{
    Import-DscResource -Module cAzure
    Import-DscResource -Module xAzure

    xAzureSubscription MSDN
    {
        Ensure = "Present"
        AzureSubscriptionName = "Windows Azure MSDN - Visual Studio Premium"
        AzurePublishSettingsFile = "C:\Temp\Azure.publishsettings"
    }

    cAzureVNetConfig VNet
    {
        DependsOn = "[xAzureSubscription]MSDN"
        ConfigurationPath = "C:\Temp\vnet.netcfg"
    }
}

SetAzureVNet -OutputPath C:\Temp\DSC -Force

Start-DscConfiguration -ComputerName 'localhost' -Path C:\Temp\DSC -Wait -Verbose

Some explanation is in place. First the Configuration keyword. The new keyword allows you to specify a PowerShell DSC configuration. This is a declarative way of specifying the desired state of a node.

Import-DscResource looks like a PowerShell cmdlet but it actually isn’t and it can only be used inside a configuration. We use Import-DscResource to import both our new module and the xAzure module from the DSC Resource Kit.

Next we use the xAzureSubscription resource to initialize our Azure subscription. Note you need the name of your subscription and your publishsettings file. Finally we call upon our new resource to configure the Azure VNet. Note that our resource has a dependency on xAzureSubscription, meaning that it should only be executed when that one has run.

That’s it! When you run the script from PowerShell ISE, this is what you should get:
Run VNet config script from PowerShell ISE

I didn’t go into DSC details here but I’ve included enough references so that you can find this information on your own.

Ronald Wildenberg

Author Ronald Wildenberg

Coming from an Artificial Intelligence background, turned developer after graduating. Interested in the tiny programming language details that make your life simpler but also in high-level designs that solve business problems in the most efficient way. And everything in between of course.

More posts by Ronald Wildenberg
25 August 2014

Leave a Reply