azureazure-automationazure-update-management-center

Azure Automation Account: Setup Update management for Azure VMs


I have created a Windows VM in Azure and configured the diagnostics settings pointing to a log analytics workspace.

Below is the VM module

resource "azurerm_windows_virtual_machine" "Lz_VM" {
  name                        = "vm-${var.subscription_type}-${var.vm_type}-${var.instance_number}"
  resource_group_name         = var.resource_group_name
  location                    = var.location
  # size                        = "Standard_F2"
  size                        = "Standard_B2S"
  admin_username              = var.username
  admin_password              = var.password
  provision_vm_agent          = true
  allow_extension_operations  = true
  network_interface_ids       = [azurerm_network_interface.vm-nic.id]
  encryption_at_host_enabled  = var.enabled_for_disk_encryption == true ? false : var.encryption_at_host_enabled

  os_disk {
    caching                 = "ReadWrite"
    storage_account_type    = "Standard_LRS"
    # disk_encryption_set_id  = var.disk_encryption_key_set_id
  }

  source_image_reference {
    publisher = "MicrosoftWindowsServer"
    offer     = "WindowsServer"
    sku       = "2016-Datacenter"
    version   = "latest"
  }

  identity {
    type  = "SystemAssigned"
  }

  tags                = var.tags
}

# This extension is needed for other extensions
resource "azurerm_virtual_machine_extension" "daa-agent" {
  name                       = "DependencyAgentWindows"
  virtual_machine_id         = azurerm_windows_virtual_machine.Lz_VM.id
  publisher                  = "Microsoft.Azure.Monitoring.DependencyAgent"
  type                       = "DependencyAgentWindows"
  type_handler_version       = "9.10"
  automatic_upgrade_enabled  = true
  auto_upgrade_minor_version = true
}

# Add logging and monitoring extensions
resource "azurerm_virtual_machine_extension" "monitor-agent" {
  depends_on = [  azurerm_virtual_machine_extension.daa-agent  ]
  name                  = "AzureMonitorWindowsAgent"
  virtual_machine_id    = azurerm_windows_virtual_machine.Lz_VM.id
  publisher             = "Microsoft.Azure.Monitor"
  type                  = "AzureMonitorWindowsAgent"
  type_handler_version  =  "1.5"
  automatic_upgrade_enabled  = true
  auto_upgrade_minor_version = true
}

resource "azurerm_virtual_machine_extension" "omsagentwin" {
  name                       = "OmsAgentForWindows"
  virtual_machine_id         = azurerm_windows_virtual_machine.Lz_VM.id
  publisher                  = "Microsoft.EnterpriseCloud.Monitoring"
  type                       = "MicrosoftMonitoringAgent"
  type_handler_version       = "1.0"
  auto_upgrade_minor_version = true

  settings = <<SETTINGS
    {
      "workspaceId": "${var.log_analytics_workspaceid}",
      "azureResourceId": "${azurerm_windows_virtual_machine.Lz_VM.id}",
      "stopOnMultipleConnections": "false"
    }
  SETTINGS

  protected_settings = <<PROTECTED_SETTINGS
    {
    "workspaceKey": "${var.log_analytics_workspace_key}"
    }
  PROTECTED_SETTINGS
}

resource "azurerm_virtual_machine_extension" "gc" {
  name                       = "AzurePolicyforWindows"
  virtual_machine_id         = azurerm_windows_virtual_machine.Lz_VM.id
  publisher                  = "Microsoft.GuestConfiguration"
  type                       = "ConfigurationforWindows"
  type_handler_version       = "1.0"
  auto_upgrade_minor_version = true
}

resource "azurerm_virtual_machine_extension" "disk-encryption" {
  count                       = var.enabled_for_disk_encryption == true ? 1 : 0    
  name                        = "de-vm-${var.subscription_type}-${var.vm_type}-${var.instance_number}"
  virtual_machine_id          = azurerm_windows_virtual_machine.Lz_VM.id
  publisher                   = "Microsoft.Azure.Security"
  type                        = "AzureDiskEncryption"
  type_handler_version        = 2.2
  auto_upgrade_minor_version  = true

  settings = <<SETTINGS
    {
        "EncryptionOperation": "EnableEncryption",
        "KeyVaultURL": "${var.keyvaultURL}",
        "KeyVaultResourceId": "${var.keyvaultResourceId}",                   
        "KeyEncryptionKeyURL": "${format("%skeys/des-key/%s", var.keyvaultURL,var.disk_encryption_key_version)}",                   
        "KekVaultResourceId": "${var.keyvaultResourceId}",
        "KeyEncryptionAlgorithm": "RSA-OAEP",
        "VolumeType": "All"
    }
SETTINGS

}

module "vm_diagnostic_settings" {
  source                    = "../diagnostic_settings"
  diagnostics_settings_name = "vm-${var.subscription_type}-${var.vm_type}-diagnostics"
  resource_id               = resource.azurerm_windows_virtual_machine.Lz_VM.id
  law_id                    = var.log_analytics_workspace_id
  logs_to_exclude           = []
  metrics_to_exclude        = []
  retention_days            = "7"
}

Log Analytics workspace has the following solution

resource "azurerm_log_analytics_solution" "update_solution" {
  solution_name         = "Updates"
  resource_group_name   = module.create_rg.rg_name
  location              = var.location
  workspace_resource_id = module.log_analytics_workspace.id
  workspace_name        = module.log_analytics_workspace.workspace_name
  
  plan {
    publisher = "Microsoft"
    product   = "OMSGallery/Updates"
  }
}

and below is the Automation account code

resource "azurerm_automation_account" "automation_account" {
  name                = "${var.name}-${var.instance_number}"
  location            = var.location
  resource_group_name = var.rg_name
  sku_name            = var.sku
  # tags                = var.tags  //Facing the issue : Too many tags
}

resource "azurerm_log_analytics_linked_service" "link_automation_account_with_log_analytics" {
  count               = var.link_log_analytics == true ? 1 : 0
  resource_group_name = var.rg_name
  workspace_id        = var.log_analytics_id
  read_access_id      = azurerm_automation_account.automation_account.id

  depends_on = [azurerm_automation_account.automation_account]
}

#Creates the schedule for updates
resource "azurerm_automation_schedule" "weekly-updates" {
  name                    = "Weekly-Sunday-6am"
  resource_group_name     = var.rg_name
  automation_account_name = azurerm_automation_account.automation_account.name
  frequency               = "Week"
  interval                = 1
  timezone                = "America/Chicago"
  start_time              = "2023-01-15T18:00:15+02:00"
  description             = "Standard schedule for updates"
  week_days               = ["Sunday"]

  depends_on = [azurerm_automation_account.automation_account]
}

Looks like the Automation Schedule don't have any runbooks associated.

enter image description here

Is that all I need to enable the Update management for Azure VMs in Automation Account? As per this article - https://claranet.medium.com/azure-update-management-solutions-overview-d167e17289df, I need something like azurerm_template_deployment which I don't understand.


Solution

  • I tried to reproduce the same as per your requirement and was able to achieve the desired results as follows:

    Use the provided resource to link a runbook to a schedule: "azurerm_automation_job_schedule"

    I made a few changes and attached the script below:

    terraform {
      required_providers {
        azurerm = {
          source = "hashicorp/azurerm"
          version = "3.39.1"
        }
      }
    }
    provider "azurerm" {
      features {}
    }
    
    resource "azurerm_resource_group" "example" {
      name     = "xxxresourcegroup"
      location = "<location>"
    }
    
    resource "azurerm_virtual_network" "example" {
      name                = "xxxxxxnetwork"
      address_space       = ["10.0.0.0/16"]
      location            = azurerm_resource_group.example.location
      resource_group_name = azurerm_resource_group.example.name
    }
    
    resource "azurerm_subnet" "example" {
      name                 = "xxxxx"
      resource_group_name  = azurerm_resource_group.example.name
      virtual_network_name = azurerm_virtual_network.example.name
      address_prefixes     = ["10.0.2.0/24"]
    }
    
    resource "azurerm_network_interface" "example" {
      name                = "xxxxxnic"
      location            = azurerm_resource_group.example.location
      resource_group_name = azurerm_resource_group.example.name
    
      ip_configuration {
        name                          = "xxxxxsubnetname"
        subnet_id                     = azurerm_subnet.example.id
        private_ip_address_allocation = "Dynamic"
      }
    }
    
    resource "azurerm_windows_virtual_machine" "example" {
      name                = "xxxxxexample"
      resource_group_name = azurerm_resource_group.example.name
      location            = azurerm_resource_group.example.location
      size                = "Standard_F2"
      admin_username      = "<user>"
      admin_password      = "<password>"
      network_interface_ids = [
        azurerm_network_interface.example.id,
      ]
    
      os_disk {
        caching              = "ReadWrite"
        storage_account_type = "Standard_LRS"
      }
    
      source_image_reference {
        publisher = "MicrosoftWindowsServer"
        offer     = "WindowsServer"
        sku       = "2019-Datacenter"
        version   = "latest"
      }
    }
    
    resource "azurerm_virtual_machine_extension" "daa-agent" {
      name                       = "DependencyAgentWindows"
      virtual_machine_id         = azurerm_windows_virtual_machine.example.id
      publisher                  = "Microsoft.Azure.Monitoring.DependencyAgent"
      type                       = "DependencyAgentWindows"
      type_handler_version       = "9.10"
      automatic_upgrade_enabled  = true
      auto_upgrade_minor_version = true
    }
    
    resource "azurerm_virtual_machine_extension" "monitor-agent" {
      depends_on = [  azurerm_virtual_machine_extension.daa-agent  ]
      name                  = "AzureMonitorWindowsAgent"
      virtual_machine_id    = azurerm_windows_virtual_machine.example.id
      publisher             = "Microsoft.Azure.Monitor"
      type                  = "AzureMonitorWindowsAgent"
      type_handler_version  =  "1.5"
      automatic_upgrade_enabled  = true
      auto_upgrade_minor_version = true
    }
    
    resource "azurerm_automation_account" "example" {
      name                = "xxxxxautomation"
      location            = azurerm_resource_group.example.location
      resource_group_name = azurerm_resource_group.example.name
      sku_name            = "Basic"
    
      tags = {
        environment = "development"
      }
    }
    
    resource "azurerm_automation_runbook" "example" {
      name                    = "xxxxx"
      location                = azurerm_resource_group.example.location
      resource_group_name     = azurerm_resource_group.example.name
      automation_account_name = azurerm_automation_account.example.name
      description             = "Run runbook"
      runbook_type            = "<runbooktype>"
    
      publish_content_link {
        uri = "https://raw.githubusercontent.com/Azure/azure-quickstart-templates/c4935ffb69246a6058eb24f54640f53f69d3ac9f/101-automation-runbook-getvms/Runbooks/Get-AzureVMTutorial.ps1" //As per MsDoc example
      }
    }
    
    resource "azurerm_log_analytics_workspace" "example" {
      name                = "xxxxxx"
      location            = azurerm_resource_group.example.location
      resource_group_name = azurerm_resource_group.example.name
      sku                 = sku
      retention_in_days   = 30 //userdefined
    }
    resource "azurerm_log_analytics_solution" "example" {
      solution_name         = "xxxxx"
      location              = azurerm_resource_group.example.location
      resource_group_name   = azurerm_resource_group.example.name
      workspace_resource_id = azurerm_log_analytics_workspace.example.id
      workspace_name        = azurerm_log_analytics_workspace.example.name
    
      plan {
        publisher = "Microsoft"
        product   = "OMSGallery/xxxxxsolution"
      }
    }
    
    resource "azurerm_log_analytics_linked_service" "example" {
      resource_group_name = azurerm_resource_group.example.name
      workspace_id        = azurerm_log_analytics_workspace.example.id
      read_access_id      = azurerm_automation_account.example.id
      depends_on = [azurerm_automation_account.example]
    }
    
    resource "azurerm_automation_job_schedule" "example" {
      resource_group_name     = azurerm_resource_group.example.name
      automation_account_name = azurerm_automation_account.example.name
      runbook_name            = azurerm_automation_runbook.example.name
      schedule_name           = azurerm_automation_schedule.example.name
      depends_on = [azurerm_automation_runbook.example,azurerm_automation_schedule.example]
    }
    
    resource "azurerm_automation_schedule" "example" {
      name                    = "xxxxxx-automation-schedule"
      resource_group_name     = azurerm_resource_group.example.name
      automation_account_name = azurerm_automation_account.example.name
      frequency               = "Week" // as per requirement
      interval                = 1
      timezone                = "UTC" //default timeZone
      week_days               = ["Saturday"]
      description             = "Perform each Saturday in UTC."
      depends_on = [
           azurerm_automation_account.example,
           azurerm_automation_runbook.example
     ]
    }
    

    terraform init:

    enter image description here

    terraform plan:

    enter image description here

    terraform apply:

    enter image description here

    Linked runbook to a schedule successfully after deployment:

    enter image description here

    Update management updated with the defined log analytics workspace:

    enter image description here

    Refer for more information: terraform registry examples