教程:使用 Azure Functions 创建预维护和维护后事件

适用于:✔️ Windows VM ✔️ Linux VM ✔️ 本地环境 ✔️ Azure VM ✔️ 已启用 Azure Arc 的服务器。

本教程介绍如何使用 Azure Functions 创建预维护和维护后事件,以在计划的修补工作流中启动和停止虚拟机(VM)。

本教程介绍如何执行下列操作:

  • 创建函数应用。
  • 创建函数。
  • 创建事件订阅。

先决条件

  1. 确保使用的是 PowerShell 7.4 运行手册。

  2. 向适当的托管标识分配权限。 Runbook 可以使用自动化帐户的系统分配的托管标识或用户分配的托管标识。

    以下脚本示例(启动和停止 VM)需要具有这些特定权限的虚拟机参与者角色或自定义角色:

    • Microsoft.Compute/virtualMachines/start/action
    • Microsoft.Compute/virtualMachines/deallocate/action
    • Microsoft.Compute/虚拟机/重启/操作
    • Microsoft.Compute/virtualMachines/powerOff/action

    可以使用 Azure 门户或 Azure PowerShell cmdlet 为每个标识分配权限:

    若要分配权限,请按照 使用 Azure 门户分配 Azure 角色中的步骤作。


  1. 导入Az.ResourceGraph模块。 确保将模块更新到 2.0.3 版的 ThreadJob。

创建函数应用

  1. 按照步骤 创建函数应用。

  2. 转到资源并使用以下步骤加载依赖项。

    注意

    第一次时只需要加载依赖项。 如果 PowerShell 依赖项无法加载,请检查 Az 和 Az.ResourceGraph 的最新版本。

    1. 对于 Function App,请选择 “应用文件”。

    2. 在 host.json下 ,将 ManagedDependecy 设置为 True ,然后选择 requirements.psd1。

    3. 在 requirements.psd1 下,粘贴以下代码:

      @{
      'Az'='12.*' 
      'Az.ResourceGraph'='1.0.0' 
      'Az.Resources'='6.*' 
      'ThreadJob' = '2.*'
      }
      
    4. 选择“保存”。

  3. 从“概述”选项卡重启函数应用,加载 requirements.psd1 文件中提到的依赖项。

创建函数

  1. 创建函数应用后,转到资源,转到 “概述”,然后选择“ 在 Azure 门户中创建”。

  2. 在 “创建函数 ”窗格中,进行以下选择:

    1. 在 “选择开发环境”下,对于 开发环境,请选择在门户中开发。

    2. 在 “选择模板”下,选择 事件网格。

    3. 在 “模板详细信息”下,对于 “新建函数”,选择名称。 然后选择“创建”。

      显示用于创建函数的选择的屏幕截图。

  3. 在 “事件网格函数 ”窗格中,从左侧菜单中选择 “Code+Test ”。 粘贴以下代码,然后选择“ 保存”。

    # Make sure that you're using eventGridEvent for parameter binding in the Azure function.
    param($eventGridEvent, $TriggerMetadata)
    
    Connect-AzAccount -Environment AzureChinaCloud -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. 在左侧菜单中,选择 “集成”。 对于 触发器,请输入 事件触发器参数名称 值。 使用“代码+测试”窗口中给定的参数名称。 在此示例中,参数为 eventGridEvent.

    显示用于事件触发器的参数名称录入的屏幕截图。

  5. 选择“保存”。

创建事件订阅

  1. 登录到 Azure 门户,然后转到“Azure 更新管理器”。

  2. 在“ 管理”下,选择“ 计算机>维护配置”。

  3. 在“ 维护配置 ”窗格中,选择配置。

  4. 在“设置”下,选择“事件”。

  5. 选择 “+事件订阅 ”以创建维护前或维护后事件。

  6. 在“ 创建事件订阅 ”窗格的“ 事件订阅详细信息 ”部分中,提供适当的名称。 将架构保留为“事件网格架构”。

  7. 在“事件类型”部分中,对于“筛选事件类型”,请选择“预维护事件”或“维护后事件”。

  8. 在 “终结点详细信息 ”部分中,选择 Azure 函数 终结点,然后选择“ 配置终结点”。

  9. 提供相应的详细信息,例如资源组和用于触发事件的函数应用。

  10. 选择创建。

还可以使用 Azure 存储帐户和事件中心来存储、发送和接收事件。 有关详细信息,请参阅有关 使用 Azure 门户在 Azure 存储中创建队列 和 创建事件中心的 快速入门。