如何在自托管Windows代理的Azure DevOps管道运行Azure CLI任务
问题场景
运行在自托管Windows代理上的Azure DevOps管道,为了管理Azure云资源配置了Azure CLI任务。前置步骤已经完成了Azure CLI的安装,但AzureCLI任务始终执行失败:
- 安装Azure CLI的步骤执行成功
- 后续的AzureCLI任务未执行任何内部代码就直接报错,连脚本里第一行的"Hello, World!"输出都没有
2021-03-05T14:50:02.5986237Z ##[error]Azure CLI 2.x is not installed on this machine. 2021-03-05T14:50:02.6391547Z ##[error]Script failed with error: Error: Unable to locate executable file: 'az'. Please verify either the file path exists or the file can be found within a directory specified by the PATH environment variable. Also verify the file has a valid extension for an executable file.
根据官方规则,代理上安装新软件后必须重启代理才能让新软件生效,且Azure CLI安装完成后需要重新打开PowerShell会话才能识别到az命令。当前的核心矛盾是AzureCLI任务无法定位已安装完成的Azure CLI可执行文件,需要找到稳定的修复方案让AzureCLI任务可以正常运行。
已尝试的无效方案
- 通过PowerShell手动配置Azure CLI的系统PATH,PATH配置本身生效,但AzureCLI任务依旧执行失败
- 在安装Azure CLI的脚本中直接运行Azure命令可以正常执行,但这种方式需要手动配置Azure登录凭据,无法使用AzureCLI任务内置的服务主体认证能力
- 尝试重启虚拟机上的Microsoft Agent服务,但系统服务列表里找不到文档提及的代理相关服务
- 在Azure CLI任务前增加等待延迟,无效果
- 替换为微软托管代理可以100%正常运行,但不符合公司合规要求,无法采用
管道现有配置
trigger: branches: exclude: - master pool: name: SelfHosted-AgentPool vmImage: 'windows-latest' variables: environment.name: 'Test' stages: - stage: build_and_deploy jobs: - deployment: VMBackup_Testing displayName: "Enable Backup Protection" environment: '$(environment.name)' strategy: runOnce: deploy: steps: - checkout: self - task: PowerShell@2 inputs: filePath: '$(System.DefaultWorkingDirectory)/Templates/Snippets/InstallAzureCLI.ps1' - task: AzureCLI@2 inputs: workingDirectory: 'C:\Program Files (x86)\Microsoft SDKs\Azure\CLI2\wbin' azureSubscription: 'XXX' scriptType: 'ps' scriptLocation: 'scriptPath' scriptPath: '$(System.DefaultWorkingDirectory)/Templates/Snippets/EnableBackupProtection.ps1'
脚本内容
Azure CLI安装脚本:
# 下载并安装Azure CLI Invoke-WebRequest -Uri https://azcliprod.blob.core.windows.net/msi/azure-cli-2.19.1.msi -OutFile .\AzureCLI.msi Start-Process msiexec.exe -Wait -ArgumentList "/I AzureCLI.msi /quiet" rm .\AzureCLI.msi # 为PowerShell会话更新PATH配置 setx /M PATH "$env:Path += ;C:\Program Files (x86)\Microsoft SDKs\Azure\CLI2\wbin" # 校验Azure CLI路径是否存在 Test-Path -Path "C:\Program Files (x86)\Microsoft SDKs\Azure\CLI2\wbin" # 重新加载当前Shell的PATH配置 $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") # 校验Azure CLI可用性 az version
Azure命令执行脚本:
# 校验脚本是否启动 Write-Host "Hello, World!" # 配置备份保护的Azure CLI命令 az backup protection enable-for-vm ` --resource-group XXX ` --vault-name XXXX ` --vm $(az vm show -g XXX -n XXX --query id) ` --policy-name DailyBackup
修复方案
根因定位
自托管代理的核心进程是在Azure CLI安装之前启动的,进程会在启动时抓取当时的系统环境变量快照,后续修改的全局PATH配置只会影响新启动的进程,不会被已经运行的代理进程感知。之前在单个PowerShell步骤里临时修改的PATH变量,只对当前步骤的进程有效,无法传递到后续启动的AzureCLI任务进程中,导致任务启动时找不到az可执行文件。
找不到Windows服务列表中的代理项,基本是因为代理是通过本地终端手动启动的,不是以Windows服务模式安装运行的。无重启代理的快速修复
如果需要保留管道内动态安装Azure CLI的流程,不需要提前在主机上预装,只需要在Azure CLI安装步骤的末尾,追加一行命令将az路径注入到当前管道会话的环境变量中:Write-Host "##vso[task.prependpath]C:\Program Files (x86)\Microsoft SDKs\Azure\CLI2\wbin"这个命令会直接把Azure CLI的路径添加到当前代理的管道会话环境里,后续启动的所有任务进程都会自动继承这个路径,不需要重启代理,也不需要手动修改全局系统PATH。
生产环境稳定配置方式
符合合规要求的长期配置,应该直接在自托管代理主机上提前预装Azure CLI,安装完成后完全重启代理进程:- 如果代理是手动启动的,直接关闭启动代理的终端窗口,重新执行代理启动命令即可
- 移除AzureCLI任务里手动指定的
workingDirectory配置,任务本身不需要手动指定az的工作路径,只要进程环境变量包含az路径就能正常识别 - 移除安装脚本里的全局PATH修改逻辑,只要代理进程启动时能读取到系统PATH里的az路径,就可以正常调用
验证方法
配置完成后可以在AzureCLI任务前加一个临时的PowerShell步骤,执行Get-Command az命令,如果能正常返回az.exe的完整路径,说明环境变量传递正常,AzureCLI任务就可以正常使用内置的服务主体认证运行,不需要手动在脚本里配置Azure登录凭据。
内容的提问来源于stack exchange,提问作者Falco

