Azure Pipeline跨租户部署Terraform遇订阅未找到错误求助
问题场景
拥有两个Azure租户:
- tenantA:仅用于存储代码仓库和Pipeline,无任何订阅,只能通过
az login --allow-no-subscriptions登录 - tenantB:用于部署基础设施,存储Terraform后端状态文件
手动通过az login --service-principal -u "spn_id" -p "spn_secret" --tenant "tenantB_id"登录tenantB后,执行Terraform命令正常。但通过tenantA的Azure Pipeline自动化执行,或登录tenantA后执行Terraform时,出现错误:
Error: Error building ARM Config: obtain subscription(id) from Azure CLI: parsing json result from the Azure CI: waiting for the Azure CLI: exit status 1: ERROR: Subscription 'id' not found. Check the spelling and try again.
核心原因
Terraform默认优先尝试从Azure CLI的当前上下文获取订阅信息,但tenantA本身无订阅,或Pipeline中CLI上下文未正确切换到tenantB的订阅,导致无法找到指定的tenantB订阅ID。同时,Terraform后端初始化阶段可能也依赖CLI上下文,未使用配置的服务主体信息。
解决方案
1. 修复Pipeline中的CLI上下文切换
在Pipeline的登录步骤后,显式设置tenantB的订阅,确保后续操作都使用该订阅上下文:
# 登录tenantB的服务主体 az login --service-principal -u "$SPN_ID" -p "$SPN_SECRET" --tenant "$TENANTB_ID" # 切换到tenantB的目标订阅 az account set --subscription "$TENANTB_SUBSCRIPTION_ID" # 可选:验证当前订阅 az account show
2. 完善Terraform后端配置,强制使用服务主体认证
Terraform的azurerm后端初始化时,默认也会尝试读取CLI上下文信息,需在backend配置中添加服务主体认证参数,避免依赖CLI:
terraform { required_providers { azurerm = { source = "hashicorp/azurerm" } } backend "azurerm" { tenant_id = "tenantB_id" subscription_id = "tenantB_subscription_id" resource_group_name = "resource_group_name" storage_account_name = "storage_account_name" container_name = "container_name" key = "key_name" # 添加服务主体认证信息 client_id = "tenantA_service_principal_id" client_secret = "tenantA_service_principal_secret" } } provider "azurerm" { features {} client_id = "tenantA_service_principal_id" client_secret = "tenantA_service_principal_secret" tenant_id = "tenantB_id" subscription_id = "tenantB_subscription_id" }
3. 使用环境变量传递敏感信息(推荐)
避免在代码中硬编码敏感信息,通过Pipeline变量或环境变量传递,Terraform会自动读取这些变量:
- 设置以下环境变量:
ARM_CLIENT_ID:tenantA中创建的服务主体IDARM_CLIENT_SECRET:服务主体密钥ARM_TENANT_ID:tenantB的租户IDARM_SUBSCRIPTION_ID:tenantB的订阅ID
修改后的Terraform配置可简化为:
terraform { required_providers { azurerm = { source = "hashicorp/azurerm" } } backend "azurerm" { tenant_id = "tenantB_id" subscription_id = "tenantB_subscription_id" resource_group_name = "resource_group_name" storage_account_name = "storage_account_name" container_name = "container_name" key = "key_name" } } provider "azurerm" { features {} }
4. 确保服务主体权限正确
确认tenantA中创建的服务主体在tenantB中拥有足够权限:
- 对tenantB的目标订阅有参与者或更高权限
- 对存储Terraform状态的存储账户有存储账户参与者或Blob数据所有者权限
内容的提问来源于stack exchange,提问作者mauek unak

