You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在VSTS构建的Azure PowerShell中并行执行Start-AzureVM?

解决Azure DevOps中PowerShell并行启动DevTest Lab虚拟机的上下文问题

你遇到的问题确实很典型:Start-Job创建的独立PowerShell进程不会继承父进程的Azure登录上下文,所以子作业里的Start-AzureRmVM会因为未授权失败。而且受限于托管代理和Azure DevOps任务并行的限制,我们可以用PowerShell Runspace池来实现并行,同时复用已有的Azure登录会话,不需要重新认证。

下面是两种可行的解决方案,优先推荐Runspace池的方式:

方案一:使用RunspacePool实现高效并行(推荐)

Runspace是PowerShell的执行环境,RunspacePool可以让我们在同一个进程内创建多个并行执行的Runspace,它们能共享父进程的Azure上下文,不需要额外登录。这种方式比Start-Job更轻量、性能更好。

修改后的脚本示例:

[cmdletbinding()]
param (
    [ValidateSet("Start","Stop")][string]$Action,
    $labName = "DevTestLab",
    $labResourceGroup = "DevTestLabRG"
)

if ($Action -eq "Start") {
    Write-Verbose "Starting the domain controller first"
    # 启动域控制器并等待就绪
    $dcVM = Get-AzureRmResource | Where-Object {
        $_.Name -match "dc" -and 
        $_.ResourceType -eq "Microsoft.Compute/virtualMachines" -and 
        $_.ResourceGroupName -match $labResourceGroup
    }
    $dcVM | Start-AzureRmVM
    # 等待DC启动完成(可选,但建议加,避免其他VM依赖DC时出问题)
    do {
        Start-Sleep -Seconds 10
        $dcStatus = Get-AzureRmVM -Name $dcVM.Name -ResourceGroupName $dcVM.ResourceGroupName -Status
    } while ($dcStatus.Statuses.Code -notcontains "PowerState/running")

    Write-Verbose "Starting other machines using RunspacePool"
    $nonDCVMs = Get-AzureRmResource | Where-Object {
        $_.Name -notmatch "dc" -and 
        $_.ResourceType -eq "Microsoft.Compute/virtualMachines" -and 
        $_.ResourceGroupName -match $labResourceGroup
    }

    # 创建Runspace池,设置并行度(根据代理性能调整,比如4)
    $runspacePool = [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspacePool(1, 4)
    $runspacePool.Open()
    $jobs = @()

    foreach ($vm in $nonDCVMs) {
        $ps = [PowerShell]::Create().AddScript({
            param($vmObj)
            Start-AzureRmVM -Name $vmObj.Name -ResourceGroupName $vmObj.ResourceGroupName
        }).AddArgument($vm)
        $ps.RunspacePool = $runspacePool
        $jobs += [PSCustomObject]@{
            PowerShell = $ps
            Result = $ps.BeginInvoke()
        }
    }

    # 等待所有Runspace完成
    foreach ($job in $jobs) {
        $job.PowerShell.EndInvoke($job.Result)
        $job.PowerShell.Dispose()
    }

    # 清理Runspace池
    $runspacePool.Close()
    $runspacePool.Dispose()
}

关键说明:

  • RunspacePool在同一个进程内运行,直接复用父进程已有的Azure登录上下文,不需要额外认证
  • 可以通过CreateRunspacePool的参数控制并行数量(第一个是最小Runspace数,第二个是最大),避免代理资源耗尽
  • 相比Start-Job,Runspace的开销更小,执行效率更高

方案二:在Start-Job中重新导入Azure上下文

如果不想改用Runspace,也可以在父进程中导出Azure登录上下文,然后在子作业里导入。这种方式需要处理临时文件,但实现起来更简单:

修改后的脚本片段:

if ($Action -eq "Start") {
    Write-Verbose "Starting the domain controller first"
    # 启动DC逻辑不变...

    # 导出当前Azure上下文到临时文件
    $contextPath = Join-Path $env:TEMP "azure_context.json"
    Save-AzureRmContext -Path $contextPath -Force

    Write-Verbose "Starting other machines in the lab as background jobs"
    foreach ($AzureRMResource in Get-AzureRmResource | Where-Object {
        $_.Name -notmatch "dc" -and 
        $_.ResourceType -eq "Microsoft.Compute/virtualMachines" -and 
        $_.ResourceGroupName -match $labResourceGroup
    } ) {
        Start-Job {
            param($vmResource, $ctxPath)
            # 导入Azure上下文
            Import-AzureRmContext -Path $ctxPath
            Start-AzureRmVM -Name $vmResource.Name -ResourceGroupName $vmResource.ResourceGroupName
        } -ArgumentList $AzureRMResource, $contextPath
    }

    # 等待所有作业完成并清理
    Get-Job | Wait-Job
    Get-Job | Receive-Job
    Get-Job | Remove-Job
    # 删除临时上下文文件
    Remove-Item $contextPath -Force
}

关键说明:

  • Save-AzureRmContext会把当前登录的Azure会话保存到文件,子作业通过Import-AzureRmContext加载
  • 注意临时文件的权限,托管代理的TEMP目录是安全的,作业完成后要删除避免残留
  • 这种方式每个子作业都会重新加载上下文,开销比Runspace大,适合VM数量不多的场景

额外建议

  • 不管用哪种方式,都建议给DC添加启动后的等待逻辑(比如检查PowerState),避免其他VM依赖DC但DC还未完全就绪的问题
  • 可以根据托管代理的CPU/内存情况调整并行数量,避免资源过载
  • 如果使用的是较新的Az模块(代替AzureRm),只需要把AzureRm相关的命令替换成Az即可,逻辑完全一致

内容的提问来源于stack exchange,提问作者Mark Allison

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:44:22