Desired State Configuration Azure Extension

 

If you read this blog you already heard about few tools in order to apply a Desired State Configuration script to your system. Today i want to write about Azure DSC Extension. This extension, can be used in two ways. The first, and the crappiest, is using the GUI, and you know what? I won’t spend much time on it. And the second one is using PowerShell Azure SDK!

So let’s take a look at this DSC Azure Extension!

 

Azure DSC Extension Portal

Let’s open Virtual Machines parameters in Azure portal

[su_frame align=”center”]01[/su_frame]

Once parameters appears in the left window, click on Extensions and on the next window, you’ll see a BGInfo extension and a DSC one (if you already apply a DSC configuration).

[su_frame align=”center”]02[/su_frame]

If you want to deploy a new DSC configuration, just click on Add and choose the Microsoft PowerShell DSC extension in the list.

[su_frame align=”center”]03[/su_frame]

A new window with few blah blah appears explaining what DSC is, why you should use it, and advertise you that PowerShell v5 will be installed alongside this extension.

[su_frame align=”center”]04[/su_frame]

Click on create. And fill all fields on the next Window in order to have the configuration applied.

Please note that if you already have a configuration applied, it’ll be remplaced by this one.

[su_frame align=”center”]05[/su_frame]

And now your configuration will apply (or not)!

Apply a configuration with Desired State Configuration

The first thing to do is to get the last Azure PowerShell SDK on github.

Once this is done let’s build a configuration in order to enable IIS

configuration NewWebsite { 
    param ( 
        [string[]]$NodeName = 'localhost'
    ) 
    # Import the module that defines custom resources 
    Import-DscResource -Module xWebAdministration 

    Node $NodeName { 

        # Install the IIS role 
        WindowsFeature IIS { 
            Ensure          = "Present" 
            Name            = "Web-Server" 
        } 

        # Install the ASP .NET 4.5 role 
        WindowsFeature AspNet45 { 
            Ensure          = "Present" 
            Name            = "Web-Asp-Net45" 
        }
    }
}

Please look that you don’t have to generate a MOF file, it’s the Azure DSC Extension that will generate it. Also, do not forget to install xWebAdministration resource 😉

Now we will use DSC to setup the environment, create the Affinity group, the service the VM and apply the DSC Configuration to the VM ! To do this task, the configuration script and all the needed resources will be merged into a single .zip file and pushed to a storage account and downloaded by the Azure DSC extension.

Configuration CreateAzure {
    param (
        [String]$VMName = "Demo-VMDSC",
        [String]$AffinityGroup = "TestAffinity",
        [String]$AffinityGroupLocation = 'East US',
        [String]$AffinityGroupDescription = 'Affinity Group',
        [String]$StorageAccountName = "srcxf01",
        [String]$ScriptExtensionsFiles = 'dscrepo1',
        [String]$ServiceName = "TestDSCService",
        [String]$ServiceDescription = 'Demo Service',
        [PSCredential]$Credential = (Get-Credential)
    )

   Import-DscResource -ModuleName xAzure

    Node localhost {

        xAzureSubscription Demotest {
            Ensure = 'Present'
            AzureSubscriptionName = '<id>'
            AzurePublishSettingsFile = 'C:\PowerShell\account.publishsettings'
        }

        xAzureAffinityGroup DemotestAffinity {
            Ensure = 'Present'
            Name = $AffinityGroup
            Location = $AffinityGroupLocation
            Label = $AffinityGroup
            Description = $AffinityGroupDescription
            DependsOn = '[xAzureSubscription]Demotest'
        }

        xAzureStorageAccount DemotestStorage {
            Ensure = 'Present'
            StorageAccountName = $StorageAccountName
            AffinityGroup = $AffinityGroup
            Container = $ScriptExtensionsFiles
            Folder = 'C:\PowerShell\demo-1\ExtensionFiles'
            Label = $StorageAccountName
            DependsOn = '[xAzureAffinityGroup]DemotestAffinity'
        }

        xAzureService DemotestService {
            Ensure = 'Present'
            ServiceName = $ServiceName
            AffinityGroup = $AffinityGroup
            Label = $ServiceName
            Description = $ServiceDescription
            DependsOn = '[xAzureStorageAccount]DemotestStorage'
        }
      
        xAzureVM DemotestVM {
            Ensure = 'Present'
            Name = $VMName
            ImageName = 'a699494373c04fc0bc8f2bb1389d6106__Windows-Server-2012-Datacenter-201502.01-en.us-127GB.vhd'
            ServiceName = $ServiceName
            StorageAccountName = $StorageAccountName
            Windows = $True
            Credential = $Credential
            InstanceSize = 'Medium'
            DependsOn = '[xAzureService]DemotestService'
        }

        xAzureVMDscConfiguration TestFiles {
            StorageAccountName       = $StorageAccountName
            ConfigurationPath        = 'C:\PowerShell\demo-1\ExtensionFiles\Breizhcamp.ps1'
            DependsOn = '[xAzureVM]DemotestVM'
        }

        xAzureVMDscExtension DSCTest  {
            VMName = $VMName
            ServiceName = $ServiceName
            ConfigurationArchive = 'Breizhcamp.ps1.zip'
            StorageAccountName = $StorageAccountName
            Configuration = 'NewWebsite'
            DependsOn = '[xAzureVMDscConfiguration]TestFiles'
        }
    }
}

$ConfigData = @{
    AllNodes = @(
        @{
            NodeName = 'localhost'
            PSDscAllowPlainTextPassword=$true
        }
    )
}

# Création du fichier MOF
sl 'C:\PowerShell\demo-1'
CreateAzure -ConfigurationData $ConfigData

# Exécution du fichier MOF
Start-DscConfiguration -ComputerName 'localhost' -wait -force -verbose -path C:\PowerShell\demo-1\CreateAzure

After a few minutes, your VM is now deployed with the assigned configuration as you can see in your verbose screen

[su_frame align=”center”]06 07[/su_frame]

Troubleshooting

In order to detect a failure or a success in your deployment, Azure team has provided you a cmdlet for that: Get-AzureVMDscExtensionStatus

The first thing to do is to create an object containing all the VM informations and properties.

$vm = Get-AzureVM -ServiceName TestDSCService -Name Demo-VMDSC
PS C:\ $vm


DeploymentName              : TestDSCService
Name                        : Demo-VMDSC
Label                       : 
VM                          : Microsoft.WindowsAzure.Commands.ServiceManagement.Model.PersistentVM
InstanceStatus              : ReadyRole
IpAddress                   : 
InstanceStateDetails        : 
PowerState                  : Started
InstanceErrorCode           : 
InstanceFaultDomain         : 0
InstanceName                : Demo-VMDSC
InstanceUpgradeDomain       : 0
InstanceSize                : Medium
HostName                    : Demo-VMDSC
AvailabilitySetName         : 
DNSName                     : 
Status                      : ReadyRole
GuestAgentStatus            : Microsoft.WindowsAzure.Commands.ServiceManagement.Model.GuestAgentStatus
ResourceExtensionStatusList : {Microsoft.Compute.BGInfo, Microsoft.Powershell.DSC}
PublicIPAddress             : 
PublicIPName                : 
PublicIPDomainNameLabel     : 
PublicIPFqdns               : {}
NetworkInterfaces           : {}
VirtualNetworkName          : 
ServiceName                 : TestDSCService
OperationDescription        : Get-AzureVM
OperationId                 : 0f7789e70ac69c55b311307955f566bd
OperationStatus             : OK

Once you get it, you just have to pipe $vm to the cmdlet

$vm | Get-AzureVMDscExtensionStatus

And you will get some useful informations.

ServiceName         : TestDSCService
Name                : Demo-VMDSC
Status              : Success
StatusCode          : 1
Timestamp           : 5/11/2015 9:13:48 AM
StatusMessage       : DSC configuration was applied successfully.
DscConfigurationLog : {Perform operation 'Invoke CimMethod' with following parameters, ''methodName' = SendConfigurationApply,'className' = MSFT_DSCLocalConfigurationManager,'namespaceName' = root/Microsoft/Windows/DesiredStateConfiguration'., 
                      An LCM method call arrived from computer DEMO-VMDSC with user sid S-1-5-18., [DEMO-VMDSC]: LCM:  [ Start  Set      ], [DEMO-VMDSC]: LCM:  [ Start  Resource ]  [[WindowsFeature]IIS]...}

Note that Status is Success and DscConfigurationLog contains all the verbose messages from LCM!

($vm | Get-AzureVMDscExtensionStatus).DscConfigurationLog
Perform operation 'Invoke CimMethod' with following parameters, ''methodName' = SendConfigurationApply,'className' = MSFT_DSCLocalConfigurationManager,'namespaceName' = root/Microsoft/Windows/DesiredStateConfiguration'.
An LCM method call arrived from computer DEMO-VMDSC with user sid S-1-5-18.
[DEMO-VMDSC]: LCM:  [ Start  Set      ]
[DEMO-VMDSC]: LCM:  [ Start  Resource ]  [[WindowsFeature]IIS]
[DEMO-VMDSC]: LCM:  [ Start  Test     ]  [[WindowsFeature]IIS]
[DEMO-VMDSC]:                            [[WindowsFeature]IIS] The operation 'Get-WindowsFeature' started: Web-Server
[DEMO-VMDSC]:                            [[WindowsFeature]IIS] The operation 'Get-WindowsFeature' succeeded: Web-Server
[DEMO-VMDSC]: LCM:  [ End    Test     ]  [[WindowsFeature]IIS]  in 47.5130 seconds.
[DEMO-VMDSC]: LCM:  [ Start  Set      ]  [[WindowsFeature]IIS]
[DEMO-VMDSC]:                            [[WindowsFeature]IIS] Installation started...
[DEMO-VMDSC]:                            [[WindowsFeature]IIS] Continue with installation?
[DEMO-VMDSC]:                            [[WindowsFeature]IIS] Prerequisite processing started...
[DEMO-VMDSC]:                            [[WindowsFeature]IIS] Prerequisite processing succeeded.
[DEMO-VMDSC]:                            [[WindowsFeature]IIS] Installation succeeded.
[DEMO-VMDSC]:                            [[WindowsFeature]IIS] successfully installed the feature Web-Server
[DEMO-VMDSC]: LCM:  [ End    Set      ]  [[WindowsFeature]IIS]  in 176.2730 seconds.
[DEMO-VMDSC]: LCM:  [ End    Resource ]  [[WindowsFeature]IIS]
[DEMO-VMDSC]: LCM:  [ Start  Resource ]  [[WindowsFeature]AspNet45]
[DEMO-VMDSC]: LCM:  [ Start  Test     ]  [[WindowsFeature]AspNet45]
[DEMO-VMDSC]:                            [[WindowsFeature]AspNet45] The operation 'Get-WindowsFeature' started: Web-Asp-Net45
[DEMO-VMDSC]:                            [[WindowsFeature]AspNet45] The operation 'Get-WindowsFeature' succeeded: Web-Asp-Net45
[DEMO-VMDSC]: LCM:  [ End    Test     ]  [[WindowsFeature]AspNet45]  in 0.7660 seconds.
[DEMO-VMDSC]: LCM:  [ Start  Set      ]  [[WindowsFeature]AspNet45]
[DEMO-VMDSC]:                            [[WindowsFeature]AspNet45] Installation started...
[DEMO-VMDSC]:                            [[WindowsFeature]AspNet45] Continue with installation?
[DEMO-VMDSC]:                            [[WindowsFeature]AspNet45] Prerequisite processing started...
[DEMO-VMDSC]:                            [[WindowsFeature]AspNet45] Prerequisite processing succeeded.
[DEMO-VMDSC]:                            [[WindowsFeature]AspNet45] Installation succeeded.
[DEMO-VMDSC]:                            [[WindowsFeature]AspNet45] successfully installed the feature Web-Asp-Net45
[DEMO-VMDSC]: LCM:  [ End    Set      ]  [[WindowsFeature]AspNet45]  in 108.0200 seconds.
[DEMO-VMDSC]: LCM:  [ End    Resource ]  [[WindowsFeature]AspNet45]
[DEMO-VMDSC]: LCM:  [ End    Set      ]
[DEMO-VMDSC]: LCM:  [ End    Set      ]    in  341.4930 seconds.
Operation 'Invoke CimMethod' complete.
Time taken for configuration job to complete is 345.504 seconds

If you got an error in this message you can also log on your VM and check C:\WindowsAzure\Logs

 Set-Location C:\WindowsAzure\Logs\Plugins\Microsoft.Powershell.DSC\1.9.0.0

[su_frame align=”center”]Sans titre[/su_frame]

The ones interesting are DscExtensionHandler.* files. If you open the last one (which is the one you applied with your script) you’ll see that everything is here to help you debug.

Get-Content .\DscExtensionHandler.0.20150511-090544.log
VERBOSE: [2015-05-11T09:06:08] File lock does not exist: begin processing
VERBOSE: [2015-05-11T09:06:09] File
C:\Packages\Plugins\Microsoft.Powershell.DSC\1.9.0.0\bin\..\DSCWork\1-InProgress.Install.dsc exists; install in progress.
VERBOSE: [2015-05-11T09:06:09] Installing DSC Extension...
VERBOSE: [2015-05-11T09:06:09] Reading handler environment from
C:\Packages\Plugins\Microsoft.Powershell.DSC\1.9.0.0\bin\..\HandlerEnvironment.jsonVERBOSE: [2015-05-11T09:06:11] Reading handler settings from
C:\Packages\Plugins\Microsoft.Powershell.DSC\1.9.0.0\RuntimeSettings\0.settings
VERBOSE: [2015-05-11T09:06:19] Retrieving WMF download information...
VERBOSE: [2015-05-11T09:06:19]     WMF Version: 5.0
VERBOSE: [2015-05-11T09:06:19]     OS Version : 6.2
VERBOSE: [2015-05-11T09:06:19]     Server OS  : True
VERBOSE: [2015-05-11T09:06:19]     64-bit OS  : True
VERBOSE: [2015-05-11T09:06:19] Verifying that WMF (KB3055377) is installed...
VERBOSE: [2015-05-11T09:06:25] WMF is already installed.
VERBOSE: [2015-05-11T09:06:25] Enabling PowerShell Remoting...
VERBOSE: [2015-05-11T09:06:25] Setting up network connections as private
VERBOSE: [2015-05-11T09:07:04] Install completed; creating
C:\Packages\Plugins\Microsoft.Powershell.DSC\1.9.0.0\bin\..\DSCWork\2-Completed.Install.dsc.
VERBOSE: [2015-05-11T09:07:04] File C:\Packages\Plugins\Microsoft.Powershell.DSC\1.9.0.0\bin\..\DSCWork\2-Completed.Install.dsc
 exists; invoking extension handler...
VERBOSE: [2015-05-11T09:07:06] Applying DSC configuration:
VERBOSE: [2015-05-11T09:07:06]     Sequence Number:              0
VERBOSE: [2015-05-11T09:07:06]     Configuration Package URL:
https://srcxf01.blob.core.windows.net/windows-powershell-dsc/Breizhcamp.ps1.zip
VERBOSE: [2015-05-11T09:07:06]     ModuleSource:
VERBOSE: [2015-05-11T09:07:06]     Configuration Module Version:
VERBOSE: [2015-05-11T09:07:06]     Configuration Container:      Breizhcamp.ps1
VERBOSE: [2015-05-11T09:07:06]     Configuration Function:       NewWebsite (0 arguments)
VERBOSE: [2015-05-11T09:07:07]     Configuration Data URL:
VERBOSE: [2015-05-11T09:07:07]     Certificate Thumbprint:       B4BDD772A54E1C3C438EADEBA77FD0C6F635C769
VERBOSE: [2015-05-11T09:07:07] Creating Working directory:
C:\Packages\Plugins\Microsoft.Powershell.DSC\1.9.0.0\bin\..\DSCWork\Breizhcamp.ps1.0
VERBOSE: [2015-05-11T09:07:07] Downloading configuration package
VERBOSE: [2015-05-11T09:07:07] Downloading
https://srcxf01.blob.core.windows.net/windows-powershell-dsc/Breizhcamp.ps1.zip?sv=2014-02-14&sr=b&sig=TQNDUDE1sTdFezlnOcLpASdQ
rEEkCFXd6kFKyXUzw0g%3D&se=2015-05-11T09%3A44%3A32Z&sp=rd to
C:\Packages\Plugins\Microsoft.Powershell.DSC\1.9.0.0\bin\..\DSCWork\Breizhcamp.ps1.0\Breizhcamp.ps1.zip
VERBOSE: [2015-05-11T09:07:08] Extracting Breizhcamp.ps1.zip
VERBOSE: [2015-05-11T09:07:08] Installing custom DSC resource modules to C:\Program Files\WindowsPowerShell\Modules
VERBOSE: [2015-05-11T09:07:08] Installing custom DSC resource module xWebAdministration
VERBOSE: [2015-05-11T09:07:08] Applying DSC configuration Breizhcamp.ps1\NewWebsite
VERBOSE: [2015-05-11T09:07:09] Writing handler status to C:\Packages\Plugins\Microsoft.Powershell.DSC\1.9.0.0\Status\0.status
VERBOSE: [2015-05-11T09:07:09] Looking for the definition of the configuration function.
VERBOSE: [2015-05-11T09:07:09] Import script as new module:C:\Packages\Plugins\Microsoft.Powershell.DSC\1.9.0.0\bin\..\DSCWork\Breizhcamp.ps1.0\Breizhcamp.ps1
VERBOSE: [2015-05-11T09:07:10] Preparing configuration arguments.
VERBOSE: [2015-05-11T09:07:10] Preparing configuration data.
VERBOSE: [2015-05-11T09:07:10] Creating MOF files.
VERBOSE: [2015-05-11T09:07:10] Executing the configuration function to generate the MOF files.
WARNING: The configuration 'NewWebsite' is loading one or more built-in resources without explicitly importing associated
modules. Add Import-DscResource -ModuleName 'PSDesiredStateConfiguration' to your configuration to avoid this message.
VERBOSE: [2015-05-11T09:07:18] Executing Start-DscConfiguration...
VERBOSE: [2015-05-11T09:07:31] Writing handler status to C:\Packages\Plugins\Microsoft.Powershell.DSC\1.9.0.0\Status\0.status

You can see every action and have a better understanding about how DSC Extension works behind the scene!

 Pester validation

In order to implement this script in your continuous delivery process, here is a Pester script to validate your deployed infrastructure.

Describe "Validate your Azure Virtual Machine creation and all the environnement" {
    
    $Certfile = 'C:\BreizhCamp\BreizhCamp.publishsettings'
    $StorageName = 'srcxf01'
    $ContainerName = 'dscrepo1'
    $Subscription = '<sid>'
    $ServiceName = "TestDSCService"
 
    Import-AzurePublishSettingsFile -PublishSettingsFile $Certfile
    Select-AzureSubscription -SubscriptionName $Subscription | Set-AzureSubscription
    Set-AzureSubscription -SubscriptionName $subscription -CurrentStorageAccountName $StorageName
    $Subscription = (Get-AzureSubscription | ? SubscriptionName -eq $Subscription )
    $StorageAccount = Get-AzureStorageAccount -StorageAccountName $StorageName
    $AffinityGroup = Get-AzureAffinityGroup -Name $StorageAccount.AffinityGroup
    $key = Get-AzureStorageKey -StorageAccountName $StorageName
    $Blob = Get-AzureStorageBlob -Context (New-AzureStorageContext -StorageAccountKey $key.Primary -StorageAccountName $StorageName) -Container $ContainerName
    $vm = Get-AzureVM -ServiceName $ServiceName -Name $VMName
    $DscExtension = $vm | Get-AzureVMDscExtensionStatus
    $AzureService = Get-AzureService -ServiceName $ServiceName
 
    It "Publish Settings file exists" {
        $Certfile | Should Exist
    }
 
    It "Azure Subscription exists" {
        $subscription | Should Not be $null
    }
    
    It "Current Storage account Affinty Group is TestAffinity" {
        $StorageAccount.AffinityGroup | Should be "TestAffinity"
    }
 
    It "Current Storage account is located on East US" {
        $AffinityGroup.Location | Should be "East US"
    }
 
    It "Current Storage account configured to srcxf01" {
        $subscription.CurrentStorageAccountName | Should be "srcxf01"
    }

    It "Service is created" {
        $AzureService.Status | Should be "Created"
    }

    It "Service Affinity group is correctly setted to TestAffinity" {
        $AzureService.AffinityGroup | Should be "TestAffinity"
    }

    It "Container Breizhcamp.ps1 exists on blob" {
        $blob.Name  | Should be "Breizhcamp.ps1"
    }
    
    It "VM name is Demo-VMDSC" {
        $vm.Name | Should be "Demo-VMDSC"
    }

    It "VM Role Size is Medium" {
        $vm.vm.RoleSize | Should be "Medium"
    }

    It "VM is correctly created and started" {
        $vm.PowerState | Should be "Started"
    }

    It "VM is correctly deployed in the good service" {
        $vm.ServiceName | Should be "TestDSCService"
    }

    It "VM Operating System is good" {
        $vm.vm.OSVirtualHardDisk.SourceImageName | Should be "a699494373c04fc0bc8f2bb1389d6106__Windows-Server-2012-Datacenter-201502.01-en.us-127GB.vhd"
    }

    It "VM should have 2 Endpoints" {
        $vm.vm.ConfigurationSets.InputEndpoints.Count | Should be 2
    }

    It "VM should have WinRM endpoint" {
        ($vm.vm.ConfigurationSets.InputEndpoints | ? { $_.LocalPort -eq 5986 }).Name | Should be "PowerShell"
    }

    It "VM should have RemoteDesktop endpoint" {
        ($vm.vm.ConfigurationSets.InputEndpoints | ? { $_.LocalPort -eq 3389 }).Name | Should be "RemoteDesktop"
    }

    It "VM iDsc Extension is installed" {
        ($vm.ResourceExtensionStatusList | ? { $_.HandlerName -eq "Microsoft.Powershell.DSC" }).status | Should be "Ready"
    }

    It "Desired State Configuration is applied" {
        $DscExtension.Status | Should be "Success"
    }
}

[su_frame align=”center”]08[/su_frame]

 

Hope this’ll help you!

See you