Edit

Share via


Tutorial: Create pre-maintenance and post-maintenance events by using Azure Functions

Applies to: ✔️ Windows VMs ✔️ Linux VMs ✔️ On-premises environment ✔️ Azure VMs ✔️ Azure Arc-enabled servers.

This tutorial explains how to create pre-maintenance and post-maintenance events to start and stop a virtual machine (VM) in a scheduled patch workflow by using Azure Functions.

In this tutorial, you learn how to:

  • Create a function app.
  • Create a function.
  • Create an event subscription.

Prerequisites

  1. Ensure that you're using a PowerShell 7.4 runbook.

  2. Assign permissions to the appropriate managed identity. The runbook can use the Automation account's system-assigned managed identity or a user-assigned managed identity.

    The following script examples (starting and stopping VMs) require the Virtual Machine Contributor role or a custom role with these specific permissions:

    • Microsoft.Compute/virtualMachines/start/action
    • Microsoft.Compute/virtualMachines/deallocate/action
    • Microsoft.Compute/virtualMachines/restart/action
    • Microsoft.Compute/virtualMachines/powerOff/action

    You can use either the Azure portal or Azure PowerShell cmdlets to assign permissions to each identity:

    To assign permissions, follow the steps in Assign Azure roles using the Azure portal.


  1. Import the Az.ResourceGraph module. Ensure that the module is updated to ThreadJob with the module version 2.0.3.

Create a function app

  1. Follow the steps to create a function app.

  2. Go to the resource and load the dependencies by using the following steps.

    Note

    You have to load the dependencies only the first time. If the PowerShell dependencies are failing to load, check the latest versions of Az and Az.ResourceGraph.

    1. For Function App, select App files.

    2. Under host.json, set ManagedDependecy to True and select requirements.psd1.

    3. Under requirements.psd1, paste the following code:

      @{
      'Az'='12.*' 
      'Az.ResourceGraph'='1.0.0' 
      'Az.Resources'='6.*' 
      'ThreadJob' = '2.*'
      }
      
    4. Select Save.

  3. Restart the function app from the Overview tab to load the dependencies that are mentioned in the requirements.psd1 file.

Create a function

  1. After you create the function app, go to the resource, go to Overview, and then select Create in Azure portal.

  2. On the Create function pane, make the following selections:

    1. Under Select development environment, for Development environment, select Develop in portal.

    2. Under Select a template, select event grid.

    3. Under Template details, for New Function, select the name. Then select Create.

      Screenshot that shows selections for creating a function.

  3. On the Event grid function pane, select Code+Test from the left menu. Paste the following code, and then select Save.

    # Make sure that you're using eventGridEvent for parameter binding in the Azure function.
    param($eventGridEvent, $TriggerMetadata)
    
    Connect-AzAccount -Identity
    
    # Install the Resource Graph module from PowerShell Gallery
    # Install-Module -Name Az.ResourceGraph
    
    $maintenanceRunId = $eventGridEvent.data.CorrelationId
    $resourceSubscriptionIds = $eventGridEvent.data.ResourceSubscriptionIds
    
    if ($resourceSubscriptionIds.Count -eq 0) {
        Write-Output "Resource subscriptions are not present."
        break
    }
    
    Write-Output "Querying ARG to get machine details [MaintenanceRunId=$maintenanceRunId][ResourceSubscriptionIdsCount=$($resourceSubscriptionIds.Count)]"
    
    $argQuery = @"
        maintenanceresources 
        | where type =~ 'microsoft.maintenance/applyupdates'
        | where properties.correlationId =~ '$($maintenanceRunId)'
        | where id has '/providers/microsoft.compute/virtualmachines/'
        | project id, resourceId = tostring(properties.resourceId)
        | order by id asc
    "@
    
    Write-Output "Arg Query Used: $argQuery"
    
    $allMachines = [System.Collections.ArrayList]@()
    $skipToken = $null
    
    do
    {
        $res = Search-AzGraph -Query $argQuery -First 1000 -SkipToken $skipToken -Subscription $resourceSubscriptionIds
        $skipToken = $res.SkipToken
        $allMachines.AddRange($res.Data)
    } while ($skipToken -ne $null -and $skipToken.Length -ne 0)
    if ($allMachines.Count -eq 0) {
        Write-Output "No Machines were found."
        break
    }
    
    $jobIDs= New-Object System.Collections.Generic.List[System.Object]
    $startableStates = "stopped" , "stopping", "deallocated", "deallocating"
    
    $allMachines | ForEach-Object {
        $vmId =  $_.resourceId
    
        $split = $vmId -split "/";
        $subscriptionId = $split[2]; 
        $rg = $split[4];
        $name = $split[8];
    
        Write-Output ("Subscription Id: " + $subscriptionId)
    
        $mute = Set-AzContext -Subscription $subscriptionId
        $vm = Get-AzVM -ResourceGroupName $rg -Name $name -Status -DefaultProfile $mute
    
        $state = ($vm.Statuses[1].DisplayStatus -split " ")[1]
        if($state -in $startableStates) {
            Write-Output "Starting '$($name)' ..."
    
            $newJob = Start-ThreadJob -ScriptBlock { param($resource, $vmname, $sub) $context = Set-AzContext -Subscription $sub; Start-AzVM -ResourceGroupName $resource -Name $vmname -DefaultProfile $context} -ArgumentList $rg, $name, $subscriptionId
            $jobIDs.Add($newJob.Id)
        } else {
            Write-Output ($name + ": no action taken. State: " + $state) 
        }
    }
    
    $jobsList = $jobIDs.ToArray()
    if ($jobsList)
    {
        Write-Output "Waiting for machines to finish starting..."
        Wait-Job -Id $jobsList
    }
    
    foreach($id in $jobsList)
    {
        $job = Get-Job -Id $id
        if ($job.Error)
        {
            Write-Output $job.Error
        }
    }
    
  4. On the left menu, select Integration. For Trigger, enter the Event Trigger parameter name value. Use the same parameter name given in the Code+Test window. In the example, the parameter is eventGridEvent.

    Screenshot that shows the entry of a parameter name for an event trigger.

  5. Select Save.

Create an event subscription

  1. Sign in to the Azure portal and go to Azure Update Manager.

  2. Under Manage, select Machines > Maintenance Configuration.

  3. On the Maintenance Configuration pane, select the configuration.

  4. Under Settings, select Events.

  5. Select +Event Subscription to create a pre-maintenance or post-maintenance event.

  6. On the Create Event Subscription pane, in the Event Subscription Details section, provide an appropriate name. Keep the schema as Event Grid Schema.

  7. In the Event Types section, for Filter to Event Types, select Pre Maintenance Event or Post Maintenance Event.

  8. In the Endpoint Details section, select the Azure Function endpoint, and then select Configure an endpoint.

  9. Provide the appropriate details, such as the resource group and the function app to trigger the event.

  10. Select Create.

You can also use Azure Storage accounts and an event hub to store, send, and receive events. For more information, see the quickstarts on creating a queue in Azure Storage and creating an event hub by using the Azure portal.