| title | Create Pre-Maintenance and Post-Maintenance Events Using Azure Functions |
|---|---|
| description | In this tutorial, you learn how to create pre-maintenance and post-maintenance events by using Azure Functions. |
| ms.service | azure-update-manager |
| ms.date | 08/21/2025 |
| ms.topic | tutorial |
| author | habibaum |
| ms.author | v-uhabiba |
| ms.custom | sfi-image-nochange |
| ms.update-cycle | 1095-days |
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:
[!div class="checklist"]
- Create a function app.
- Create a function.
- Create an event subscription.
[!INCLUDE pre-post-prerequisites.md]
-
Follow the steps to create a function app.
-
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
AzandAz.ResourceGraph.-
For Function App, select App files.
-
Under host.json, set ManagedDependecy to True and select requirements.psd1.
-
Under requirements.psd1, paste the following code:
@{ 'Az'='12.*' 'Az.ResourceGraph'='1.0.0' 'Az.Resources'='6.*' 'ThreadJob' = '2.*' } -
Select Save.
-
-
Restart the function app from the Overview tab to load the dependencies that are mentioned in the requirements.psd1 file.
-
After you create the function app, go to the resource, go to Overview, and then select Create in Azure portal.
-
On the Create function pane, make the following selections:
-
Under Select development environment, for Development environment, select Develop in portal.
-
Under Select a template, select event grid.
-
Under Template details, for New Function, select the name. Then select Create.
:::image type="content" source="./media/tutorial-using-functions/create-function.png" alt-text="Screenshot that shows selections for creating a function." lightbox="./media/tutorial-using-functions/create-function.png":::
-
-
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 } }# 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 } Start-Sleep -Seconds 30 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] $stoppableStates = "starting", "running" $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 $stoppableStates) { Write-Output "Stopping '$($name)' ..." $newJob = Start-ThreadJob -ScriptBlock { param($resource, $vmname, $sub) $context = Set-AzContext -Subscription $sub; Stop-AzVM -ResourceGroupName $resource -Name $vmname -Force -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 stop operation..." Wait-Job -Id $jobsList } foreach($id in $jobsList) { $job = Get-Job -Id $id if ($job.Error) { Write-Output $job.Error } }Invoke-AzRestMethod ` -Path "<Correlation ID from EventGrid Payload>?api-version=2023-09-01-preview" ` -Payload '{ "properties": { "status": "Cancel" } }' ` -Method PUT
-
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.:::image type="content" source="./media/tutorial-using-functions/parameter-event-grid.png" alt-text="Screenshot that shows the entry of a parameter name for an event trigger." lightbox="./media/tutorial-using-functions/parameter-event-grid.png":::
-
Select Save.
-
Sign in to the Azure portal and go to Azure Update Manager.
-
Under Manage, select Machines > Maintenance Configuration.
-
On the Maintenance Configuration pane, select the configuration.
-
Under Settings, select Events.
-
Select +Event Subscription to create a pre-maintenance or post-maintenance event.
-
On the Create Event Subscription pane, in the Event Subscription Details section, provide an appropriate name. Keep the schema as Event Grid Schema.
-
In the Event Types section, for Filter to Event Types, select Pre Maintenance Event or Post Maintenance Event.
-
In the Endpoint Details section, select the Azure Function endpoint, and then select Configure an endpoint.
-
Provide the appropriate details, such as the resource group and the function app to trigger the event.
-
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.
- Get an overview of pre-maintenance and post-maintenance events in Azure Update Manager.
- Learn more about how to create pre-maintenance and post-maintenance events.
- Learn how to manage pre-maintenance and post-maintenance events or to cancel a scheduled run.
- Learn how to create pre-maintenance and post-maintenance events by using a webhook with Automation runbooks.