教程:使用 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 依赖项无法加载,请检查 AzAz.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 存储中创建队列创建事件中心的 快速入门。