Post

Managing Azure VM Guest and host updates (Azure DevOps)

Managing Azure VM Guest and host updates (Azure DevOps)

Howdy folks,

Back again with a new blog post. This time it’s about Azure VM Guest and VM host update management during the VM deployment process. I’m writing this blog assuming you have a good understanding of how to manage Azure VM updates from Azure

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 is among one of the requirements.

There are great articles from Microsoft and as well as other consultants on how to manage windows update in Azure, this article is slightly different and explain how to achieve this via pipeline

Without further due following are the key component we need to set up.

ComponentDeployment Method
Log Analytics WorkspaceBICEP
Automation AccountBICEP
Link Automation to WorkspaceBICEP
Update SolutionBICEP
Provision Update ScheduleAzure Powershell command in the pipeline
Add enable updates for VMAzure 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 below

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 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

1
2
3
4
5
6
7
8
9
10
11
12
13
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
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@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 below yml code to deploy the main bicep file.

I have used the below YML part to deploy the main bicep file (Note: my main yml file name is management.main.bicep)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- stage: Build_Management_Services
  jobs:  
  - job: buildManagement
    steps:
    - checkout: self
    - task: AzureCLI@2  
      displayName: 'Deploy Management Bicep Template File' 
      inputs: 
        azureSubscription: $
        scriptType: 'pscore'  
        scriptLocation: 'inlineScript'  
        inlineScript: |
          az deployment sub create --location australiaeast `
          --template-file 'main/platform-services/management/management.main.bicep' `
          --parameters lzShortName=$ custShortName=$ envShortName=$ `
            enablePaaSFirewall=$ enableResourceLock=$ location=$

Provision Update Schedule and Enable VM Updates (Inside the pipeline)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
- stage: Enable_VM_Updates
  jobs:
  - job: enableVMUpdates
    steps:
    - checkout: self
    - task: AzureCLI@2
      name: enableVMBackups
      displayName: 'Enable VM Backups' 
      inputs: 
        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 $ -StartTime $StartTime -MonthInterval 1 -ResourceGroupName $ResourceGroup -DayOfWeekOccurrence $ -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 in bicep language.

Finally, there are 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 required for this to work properly
  3. Follow the component hirachy when deploying

As always feel free to reach out to me to for any questions that you have. Or let me know If I have done a mistake

Untill next time……

For more information

https://docs.microsoft.com/en-us/azure/virtual-machines/automatic-vm-guest-patching

https://docs.microsoft.com/en-us/azure/automation/update-management/enable-from-template

This post is licensed under CC BY 4.0 by the author.