Test your PowerShell DSC configuration on Azure VM

Troubleshooting and debugging PowerShell DSC configurations could be sometimes very painful. If you are preparing your DSC configuration to use in Azure or even on-premises, you would like to test it in the real environment.

One of the simplest method is to create empty Azure VM and push your configuration via PowerShell Desire State Configuration extension.  For example, if we want to create IIS Web Server joined to Active Directory domain we will need following configuration:

Configuration IISWebServer
{ 
   param 
    ( 
        [string]$NodeName,
        [string]$DomainJoinName,
        [string]$RemoteUserName,
        [PSCredential] $DomainJoinCredential
    ) 
    
    Import-DscResource -ModuleName xComputerManagement
    
    Node $NodeName
    {
        LocalConfigurationManager            
        {            
            ActionAfterReboot = 'ContinueConfiguration'            
            ConfigurationMode = 'ApplyOnly'            
            RebootNodeIfNeeded = $true            
        } 
        
        WindowsFeature IIS
        {
          Ensure = "Present"
          Name = "Web-Server"
        }

        WindowsFeature ASP
        {
          Ensure = "Present"
          Name = "Web-Asp-Net45"
        }

        WindowsFeature WebServerManagementConsole
        {
            Name = "Web-Mgmt-Console"
            Ensure = "Present"
           
        }

        xComputer IISWebServerVM {
        
            Name = $NodeName
            DomainName = $DomainJoinName
            Credential = $DomainJoinCredential
            DependsOn = "[WindowsFeature]IIS", "[WindowsFeature]ASP", "[WindowsFeature]WebServerManagementConsole"
        }

        Group AllowRemoteConnect {
            Ensure = "Present"
            GroupName = "Remote Desktop Users"
            MembersToInclude = $RemoteUserName
            Credential = $DomainJoinCredential
            DependsOn = "[xComputer]IISWebServerVM"
        }
   }

}

If you need more complex environment then just a one server you can develop an ARM Template to simplify redeployment.

Now you should organize your folders, I will use following structure:

.\dsc\xComputerManagement # DSC Experimental Resources need for xComputer: 
                          # https://github.com/PowerShell/xComputerManagement
.\dsc\IISWebServer.ps1    # DSC Configuration
                          # Name of the file should match the name of configuration inside this file
.\ConfigurationData.psd1  # Configuration Data File
.\Test-Configuration.ps1  # Deployment script

 

After it is time to zip dsc folder, upload it to Azure Blob storage and deploy Azure PowerShell DSC Extension to created Virtual Machine. Please, follow comments inside code to understand better each part:

 

#region Login to Azure Subscription

Login-AzureRmAccount
Select-AzureRmSubscription -SubscriptionId "{subscription_id}"

#endregion 

#region Artefacts Storage Parameters 

# Create Resource Group before launch of the script

$artefactsStorageResourceGroupName = "{artefacts_resource_group_name}"
$artefactsStorageName = "{artefacts_storage_name}"
$artefactsStorageLocation = "{artefacts_storage_location}"
$artefactsStorageContainerName = "{artefacts_storage_container_name}"

#endregion

#region DSC Configaration Parameters

$dscConfigurationFolder = ".\dsc\"

$dscConfigurationName = "IISWebServer"
$dscConfigurationFileName = $dscConfigurationName + ".ps1"
$dscConfigurationDataFileName = "ConfigurationData.psd1"
$dscConfigurationZipFileName = $dscConfigurationFileName + ".zip"

#endregion

#region Existing Azure VM and Extension Parameters 

$dscConfigurationArguments = @{ 
    NodeName = "localhost"
    DomainJoinName = "{ad_domain}"
    RemoteUserName = "{ad_domain_net}\{ad_domain_username}"
    DomainJoinCredential = Get-Credential 
}

# Release History : https://blogs.msdn.microsoft.com/powershell/2014/11/20/release-history-for-the-azure-dsc-extension/

$dscExtensionVersion = "2.2"

$vmResourceGroupName = "{vm_resource_group_name}"
$vmName = "{vm_name}"

#endregion

#region Prepare zip-file to upload

Add-Type -AssemblyName "System.IO.Compression.FileSystem"

if (Test-Path -Path $dscConfigurationZipFileName) {
    Remove-Item -Path $dscConfigurationZipFileName
}

[io.compression.zipfile]::CreateFromDirectory($dscConfigurationFolder, $dscConfigurationZipFileName)

#endregion


#region Upload configuration zip-file to Azure Resource Manager Blob Storage

# Create Storage Account if it does not exist

$storage = Get-AzureRmStorageAccount -ResourceGroupName $artefactsStorageResourceGroupName  `
        -Name $artefactsStorageName -ErrorAction SilentlyContinue

if (!$storage) {
    $storage = New-AzureRmStorageAccount -ResourceGroupName $artefactsStorageResourceGroupName `
                                -Name $artefactsStorageName `
                                -Location $artefactsStorageLocation `
                                -SkuName Standard_LRS
}

# Get Storage Account keys and context

$storageKeys = Get-AzureRmStorageAccountKey -ResourceGroupName $storage.ResourceGroupName -Name $storage.StorageAccountName
$storageContext = New-AzureStorageContext -StorageAccountName $storage.StorageAccountName -StorageAccountKey $storageKeys[0].Value


# Create Container if it does not exist

$storageContainer = Get-AzureStorageContainer -Name $artefactsStorageContainerName -Context $storageContext -ErrorAction SilentlyContinue

if (!$storageContainer) {
    $storageContainer = New-AzureStorageContainer -Name $artefactsStorageContainerName -Context $storageContext 
}

# Overwrite the file on blob storage

$blob = Set-AzureStorageBlobContent -File $dscConfigurationZipFileName  `
        -Container $storageContainer.Name `
        -Context $storageContext `
        -Force

#endregion

#region DSC Extension

# Update DSC Extension with new DSC configuration 

Set-AzureRmVMDscExtension -ResourceGroupName $vmResourceGroupName `
    -VMName $vmName `
    -ArchiveBlobName $blob.Name `
    -ArchiveStorageAccountName $storage.StorageAccountName `
    -ArchiveResourceGroupName $storage.ResourceGroupName `
    -ArchiveContainerName $storageContainer.Name `
    -ConfigurationName $dscConfigurationName `
    -ConfigurationArgument $dscConfigurationArguments `
    -ConfigurationData $dscConfigurationDataFileName `
    -Version $dscExtensionVersion `
    -Force

#endregion
   

 

The configuration data with PSDscAllowDomainUser and PSDscAllowPlainTextPassword can be useful during DSC debugging but in the real projects we should encrypt credentials with our own certificate.

@{

    AllNodes = 
        @(
            @{
                NodeName           = "localhost"
                PSDscAllowDomainUser = $true
                PSDscAllowPlainTextPassword = $true
            }
        )

}

The last thing is to launch the script Test-Configuration.ps1 for each modification of DSC configuration file until Success status.

You can check the detailed status with execution log and errors on Azure Portal:

Virtual Machines -> [your vm] -> Extensions -> Microsoft.PowerShell.DSC -> View detailed status

or to examine full logs inside your Virtual Machine:

C:\WindowsAzure\Logs\Plugins\Microsoft.PowerShell.DSC\[version]\

the detailed status that you see on Azure Portal is inside following folder:

C:\Packages\Plugins\Microsoft.PowerShell.DSC\[version]\Status\