Azure DevOps+ARM模板下实现VM创建时为其身份分配存储Blob访问角色的方法
核心实现思路
完全可以在单份ARM模板内通过资源依赖机制解决部署时序问题,不需要在Azure DevOps里拆分多步部署流程:
- 在VM定义阶段开启系统分配托管身份后,托管身份的主体ID会和VM的控制面配置同步生成,不需要等VM内部操作系统完成初始化就能被同模板内的其他资源引用
- 通过给角色分配资源、自定义脚本扩展配置明确的依赖关系,可以确保权限授权在自定义脚本实际执行azcopy命令前完成,不会出现权限未生效导致的访问失败
- 整个链路的部署顺序完全由ARM模板的依赖规则管控,不需要额外写Azure DevOps的步骤做权限补配
具体ARM模板配置步骤
1. 开启VM的系统分配托管身份
在VM资源的定义中添加identity配置,确保能生成可用于权限配置的托管身份主体ID,参考片段:
{ "type": "Microsoft.Compute/virtualMachines", "apiVersion": "2023-03-01", "name": "[variables('vmName')]", "location": "[resourceGroup().location]", "identity": { "type": "SystemAssigned" }, // 保留原有VM的网络、存储、规格等配置内容 }
2. 配置Storage Blob Data Contributor角色分配
新增角色分配资源,直接引用同模板内VM的托管身份主体ID,绑定到目标存储账户的访问范围,参考片段:
{ "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "name": "[guid(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), resourceId('Microsoft.Compute/virtualMachines', variables('vmName')), 'StorageBlobDataContributor')]", "scope": "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]", "properties": { // Storage Blob Data Contributor的全局固定角色ID,跨环境通用,避免角色名语言匹配问题 "roleDefinitionId": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/ba92f5b4-2d11-453d-a403-e96b0029c9fe')]", // 通过reference的Full输出获取VM托管身份的主体ID,不需要等VM完全初始化 "principalId": "[reference(resourceId('Microsoft.Compute/virtualMachines', variables('vmName')), '2023-03-01', 'Full').identity.principalId]", "principalType": "ServicePrincipal" }, "dependsOn": [ "[resourceId('Microsoft.Compute/virtualMachines', variables('vmName'))]" ] }
3. 给自定义脚本扩展添加权限依赖
修改自定义脚本扩展的配置,显式依赖前面创建的角色分配资源,确保脚本启动时权限已经完成授权,参考片段:
{ "type": "Microsoft.Compute/virtualMachines/extensions", "apiVersion": "2023-03-01", "name": "[concat(variables('vmName'), '/CustomScriptExtension')]", "location": "[resourceGroup().location]", "dependsOn": [ "[resourceId('Microsoft.Compute/virtualMachines', variables('vmName'))]", // 显式依赖角色分配资源,等权限生效后再启动脚本 "[resourceId('Microsoft.Authorization/roleAssignments', guid(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), resourceId('Microsoft.Compute/virtualMachines', variables('vmName')), 'StorageBlobDataContributor'))]" ], "properties": { "publisher": "Microsoft.Compute", "type": "CustomScriptExtension", "autoUpgradeMinorVersion": true, "settings": { // 配置对应脚本下载地址,比如azcopy的安装包、执行脚本 }, "protectedSettings": { "commandToExecute": "powershell -ExecutionPolicy Unrestricted -Command \"c:\\azcopy login --identity; c:\\azcopy copy https://mystorage.blob.core.windows.net/software C:\\Temp --recursive\"" } } }
常见踩坑提醒
- 严格将角色分配的scope限定在目标存储账户层级,不要扩大到资源组或订阅范围,遵循最小权限原则
- 不要省略角色分配对VM资源的依赖,否则ARM可能在VM的托管身份主体ID还未生成时就尝试创建角色分配,报主体ID为空的错误
- 不要省略自定义脚本扩展对角色分配的依赖,否则ARM可能提前启动自定义脚本,等权限配置完成后才生效,出现azcopy登录成功但Blob下载报403权限不足的问题
- 如果使用用户分配托管身份而非系统分配托管身份,实现逻辑完全一致:直接引用托管身份资源的主体ID做角色分配,再给自定义脚本扩展加上对应依赖即可
内容的提问来源于stack exchange,提问作者Blue Clouds
相关产品推荐
相关产品推荐

