Azure VM Guest and host updates management during the VM deployment process is becoming a standard configuration. In a recent project, there was a requirement to create an environment for a software deployment solution. And as part of the solution, automatic VM update was one of the requirements
There are great articles from Microsoft and as well as other consultants on how to manage windows updates in Azure, this article is slightly different and explains how to achieve this via pipeline
Without further ado following are the key component we need to set up.
Component | Deployment Method |
Log Analytics Workspace | BICEP |
Automation Account | BICEP |
Link Automation to Workspace | BICEP |
Update Solution | BICEP |
Provision Update Schedule | Azure Powershell command in the pipeline |
Add enable updates for VM | Azure Powershell command in the pipeline |
As you can see there is an inter-dependency between automation account and workspace. So during my provisioning process, I followed the below:
Azure VM Guest and Host Updates Deployment Flow
In this situation, I had to provision the log analytics workspace first to capture the automation account logs. And I had to use PowerShell to execute the last two stages inside the pipeline
Following are the bicep templates. All 4 components were saved in 4 separate modules and using my main bicep file I have called the 4 modules. During the bicep deployment, I had to set the dependencies to module files. Once the four modules are deployed I have added another stage to run the PowerShell scripts.
Log Analytics Workspace
// Resource Definition
resource loganalytics 'Microsoft.OperationalInsights/workspaces@2020-08-01' = {
name: workspaceName
location: location
tags: !empty(tags) ? tags : json('null')
properties: {
sku: {
name: 'PerGB2018'
capacityReservationLevel: (capacityReservation == 0) ? json('null') : capacityReservation
}
retentionInDays: retentionInDays
features: {
searchVersion: 1
enableLogAccessUsingOnlyResourcePermissions: true
}
}
}
Automation Account
resource automationAccount 'Microsoft.Automation/automationAccounts@2020-01-13-preview' = {
name: autoAccountName
location: location
tags: !empty(tags) ? tags : json('null')
identity: {
type: 'SystemAssigned'
}
properties: {
sku: {
name: sku
}
}
}
Link Automation Account with Log Analytics
// Log Analytics Workspace Automation Link
@description('The name of the resource.')
param workspaceName string
@description('Location of the resource.')
param automationAccountName string
resource logAnalyticsAutomation 'Microsoft.OperationalInsights/workspaces/linkedServices@2020-08-01' = if (!empty(automationAccountName)) {
name: '${workspaceName}/Automation'
properties: {
resourceId: resourceId('Microsoft.Automation/automationAccounts', automationAccountName)
}
}
Enable Update Solution
@description('Log Analytics Workspace ID')
param logAnalyticsId string
@description('Log Analytics Workspace Name')
param logAnalyticsName string
@description('Location')
param location string
var updateManagementName = 'Updates(${logAnalyticsName})'
resource update 'Microsoft.OperationsManagement/solutions@2015-11-01-preview' = {
name: updateManagementName
location: location
properties: {
workspaceResourceId: logAnalyticsId
}
plan: {
name: updateManagementName
publisher: 'Microsoft'
product: 'OMSGallery/Updates'
promotionCode: ''
}
}
#Note : One Special thing to not when deploying the solution. Name of the solution should be in following format 'Updates(${you_log_analytics_workspace_name})'. Otherwise the solution will not deploy and throw an error.
I have used the below YML part to deploy the main bicep file (Note: my main yml file name is management.main.bicep)
- stage: Build_Management_Services
jobs:
- job: buildManagement
steps:
- checkout: self
- task: AzureCLI@2
displayName: 'Deploy Management Bicep Template File'
inputs:
azureSubscription: ${{ parameters.azureSubscription }}
scriptType: 'pscore'
scriptLocation: 'inlineScript'
inlineScript: |
az deployment sub create --location australiaeast `
--template-file 'main/platform-services/management/management.main.bicep' `
--parameters lzShortName=${{ parameters.lzShortName }} custShortName=${{ parameters.custShortName }} envShortName=${{ variables.envShortName }} `
enablePaaSFirewall=${{ parameters.enablePaaSFirewall }} enableResourceLock=${{ parameters.enableResourceLock }} location=${{ parameters.location }}
Provision Update Schedule and Enable VM Updates (Inside the pipeline)
- stage: Enable_VM_Updates
jobs:
- job: enableVMUpdates
steps:
- checkout: self
- task: AzureCLI@2
name: enableVMBackups
displayName: 'Enable VM Backups'
inputs:
azureSubscription: ${{ parameters.azureSubscription }}
scriptType: 'pscore'
scriptLocation: 'inlineScript'
inlineScript: |
$resourceGroup=Get-AzDeploymentOperation workloads.vm.app --query properties.outputs.rgName.value -o tsv
$AutomationAccountName=Get-AzDeploymentOperation management.main --query properties.outputs.automationName.value -o tsv
$logAnalytics=Get-AzDeploymentOperation subscription.main --query properties.outputs.logWorkspaceName.value -o tsv
echo $AutomationAccountName
echo $resourceGroupName
$duration = New-TimeSpan -Hours 2
$StartTime = (Get-Date "23:00:00").Adddays(1)
Write-Host "Wait Until VM Extensions are enabled"
Start-Sleep -Seconds 180
$Schedule = New-AzAutomationSchedule -AutomationAccountName $AutomationAccountName -Name ${{ parameters.updateScheduleName }} -StartTime $StartTime -MonthInterval 1 -ResourceGroupName $ResourceGroup -DayOfWeekOccurrence ${{ parameters.weektoInstallUpdate }} -DayOfWeek Friday -TimeZone 'AUS Eastern Standard Time'
$VMIDs = (Get-AzVM -ResourceGroupName $resourceGroup).Id
New-AzAutomationSoftwareUpdateConfiguration -ResourceGroupName $resourceGroup -Schedule $Schedule -Windows -AzureVMResourceId $VMIDs -Duration $duration -IncludedUpdateClassification Critical,Security,Definition,UpdateRollup,Updates -AutomationAccountName $AutomationAccountName -Verbose
If you are re-using this code, you may need to fix the tabs accordingly in yml file as per below.
I haven’t documented the full bicep deployment and main file creation. I’m guessing If you have come this far you have a good understanding of bicep language.
Conclusion
There are a few more things to watch out
- Update solution Name – Name of the solution should be in following format ‘Updates(${you_log_analytics_workspace_name})’. Otherwise, the solution will not deploy and throw an error.
- All 6 components are required for this to work properly
- Follow the component hierarchy when deploying