I just finished the initial tests phase on automating our product release to Azure Virtual Machines using DSC, particularly with the commands described in this article, which are part of the Azure PowerShell SDK.
I can push a DSC configuration fine using PowerShell, but since this process is automated, I wanted to get feedback on how the configuration process progressed. When I call Update-AzureVM
, I get an ok but the DSC configuration happens after that, asynchronously, and I don't know how it is going unless I log into the machine (or look at the updated Azure Portal which now shows this).
I'd like to fail my automated process if the configuration fails. How can I check the status of the configuration from my script and gracefully detect success or failure?
We have added a new cmdlet Get-AzureVMDscExtensionStatus to obtain status of the running DSC configuration
This article explains the same http://blogs.msdn.com/b/powershell/archive/2015/02/27/introducing-get-azurevmdscextensionstatus-cmdlet-for-azure-powershell-dsc-extension.aspx
There are a few ways to do this. You can call the REST-based API as I described in a recent post here.
You could also use Get-AzureVM to drill into the value (just like parsing the REST response) like so:
((Get-AzureVM -ServiceName "" -Name "").ResourceExtensionStatusList | Where-Object { $_.HandlerName -eq 'Microsoft.PowerShell.DSC' }).ExtensionSettingStatus.Status
Based on @David's suggestion, I ended up creating a polling function to detect the status changes and report back to my main script.
First, I needed to find what the terminating status codes where (I need to finish my loop as soon as I detect a successful DSC operation or if any error happens).
I drilled down in the files used by the DSC Extension in the VM to find the possible status codes and based my condition on that. The status codes can be found at C:\Packages\Plugins\Microsoft.Powershell.DSC\1.4.0.0\bin\DscExtensionStatus.psm1
in any virtual machine with the DSC Extension installed. Here are the status codes as of version 1.4.0.0 of the DSC Extension:
$DSC_Status = @{
Initializing = @{
Code = 1
Message = "Initializing DSC extension."
}
Completed = @{
Code = 2
Message = "DSC configuration was applied successfully."
}
Enabled = @{
Code = 3
Message = "PowerShell DSC has been enabled."
}
RebootingInstall = @{
Code = 4
Message = "Rebooting VM to complete installation."
}
RebootingDsc = @{
Code = 5
Message = "Rebooting VM to apply DSC configuration."
}
Applying = @{
Code = 6
Message = "Applying DSC configuration to VM."
}
#
# Errors
#
GenericError = 100; # The message for this error is provided by the specific exception
InstallError = @{
Code = 101
Message = "The DSC Extension was not installed correctly, please check the logs on the VM."
}
WtrInstallError = @{
Code = 102
Message = "WTR was not installed correctly, please check the logs on the VM."
}
}
The logic in the function is somewhat convoluted because the state changes are persistent, i.e. they are not from a single DSC operation, but from the whole extension itself. Because of that, I needed to pick the status first to then try to find updates. I'm using the timestamp
field to detect a new status. Here is the code:
function Wait-AzureDSCExtensionJob
{
[CmdletBinding()]
Param(
[Parameter(Mandatory)]
[string] $ServiceName,
[int] $RefreshIntervalSeconds = 15
)
Begin
{
$statusFormat = `
@{Label = 'Timestamp'; Expression = {$_.TimestampUtc}},
@{Label = 'Status'; Expression = {"$($_.Code) - $($_.Status)"}}, `
@{Label = 'Message'; Expression = {$_.FormattedMessage.Message}}
Write-Verbose 'Getting starting point status...'
$previousStatus = Get-AzureDscStatus -ServiceName:$ServiceName
Write-Verbose "Status obtained: $($previousStatus | Format-List $statusFormat | Out-String)"
Write-Verbose 'This status will be used as the starting point for discovering new updates.'
}
Process
{
do
{
Write-Verbose "Waiting for the next check cycle. $RefreshIntervalSeconds seconds left..."
Start-Sleep -Seconds:$RefreshIntervalSeconds
$currentStatus = Get-AzureDscStatus -ServiceName:$ServiceName
if ($previousStatus.TimestampUtc -eq $currentStatus.TimestampUtc)
{
Write-Verbose 'Status has not changed since the last check.'
$statusUpdated = $false
}
else
{
Write-Verbose "Status updated: $($currentStatus | Format-List $statusFormat | Out-String)"
$previousStatus = $currentStatus
$statusUpdated = $true
}
# Script with default message codes for the DSC Extension:
# On Target VM: "C:\Packages\Plugins\Microsoft.Powershell.DSC\1.4.0.0\bin\DscExtensionStatus.psm1"
} until ($statusUpdated -and (($currentStatus.Code -eq 2) -or ($currentStatus.Code -ge 100)))
}
End
{
switch ($currentStatus.Code)
{
2 {Write-Verbose 'Configuration finished successfully.'; break}
default {throw "Configuration failed: $($currentStatus.Status)"}
}
}
}
function Get-AzureDscStatus
{
[CmdletBinding()]
Param(
[Parameter(Mandatory)]
[string] $ServiceName
)
Begin
{
$vm = Get-AzureVM -ServiceName:$ServiceName
$dscExtensionStatus = $vm.ResourceExtensionStatusList | where { $_.HandlerName -eq 'Microsoft.PowerShell.DSC' }
if (-not $dscExtensionStatus)
{
throw 'Could not find the PowerShell DSC Extension on the VM'
}
$dscExtensionStatus.ExtensionSettingStatus
}
}
I'm not very proficient in PowerShell yet, so this could probably look a bit better and be easier to read. Still, I hope it can be of use to someone in the same situation as me.
UPDATE 28/11/2014:
Microsoft has updated the DSC Extension to version 1.5.0.0 and my function broke, how nice of them. I mean... it's not as if changing the response codes is a breaking change or anything like that ;)
Here are the new status codes:
$DSC_Status = @{
Success = @{
Code = 1
Message = 'DSC configuration was applied successfully.'
}
Initializing = @{
Code = 2
Message = 'Initializing DSC extension.'
}
Enabled = @{
Code = 3
Message = 'PowerShell DSC has been enabled.'
}
RebootingInstall = @{
Code = 4
Message = 'Rebooting VM to complete installation.'
}
RebootingDsc = @{
Code = 5
Message = 'Rebooting VM to apply DSC configuration.'
}
Applying = @{
Code = 6
Message = 'Applying DSC configuration to VM.'
}
#
# Errors
#
GenericError = 1000 # The message for this error is provided by the specific exception
InstallError = @{
Code = 1001
Message = 'The DSC Extension was not installed correctly, please check the logs on the VM.'
}
WtrInstallError = @{
Code = 1002
Message = 'WTR was not installed correctly, please check the logs on the VM.'
}
OsVersionNotSupported = @{
Code = 1003
Message = 'The current OS version is not supported. The DSC Extension requires Windows Server 2012 or 2012 R2, or Windows 8.1.'
}
}
For some reason they swapped the codes and now 1
is success, while errors went up from 100
to 1000
(they surely are expecting a lot of errors on this one).