Managing Azure VM Guest and Host Updates Solution Using Azure Pipeline

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
Azure VM Guest and Host Updates Deployment Components

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

Azure VM Guest and Host Updates

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
// 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
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
  - job: buildManagement
    - checkout: self
    - task: AzureCLI@2  
      displayName: 'Deploy Management Bicep Template File' 
        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
  - job: enableVMUpdates
    - checkout: self
    - task: AzureCLI@2
      name: enableVMBackups
      displayName: 'Enable VM Backups' 
        azureSubscription: ${{ parameters.azureSubscription }}
        scriptType: 'pscore'  
        scriptLocation: 'inlineScript'
        inlineScript: | 
          $resourceGroup=Get-AzDeploymentOperation --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.


There are a few more things to watch out

  1. 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.
  2. All 6 components are required for this to work properly
  3. Follow the component hierarchy when deploying


Blogs – Arinco

[mailpoet_form id="1"]

Other Recent Blogs

Level 9, 360 Collins Street, 
Melbourne VIC 3000

Level 2, 24 Campbell St,
Sydney NSW 2000

200 Adelaide St,
Brisbane QLD 4000

191 St Georges Terrace
Perth WA 6000

Level 10, 41 Shortland Street

Part of

Arinco trades as Arinco (VIC) Pty Ltd and Arinco (NSW) Pty Ltd. © 2023 All Rights Reserved Arinco™ | Privacy Policy
Arinco acknowledges the Traditional Owners of the land on which our offices are situated, and pay our respects to their Elders past, present and emerging.

Get started on the right path to cloud success today. Our Crew are standing by to answer your questions and get you up and running.