Azure特定RG拥有Contributor权限却无法用Terraform创建资源
我在Azure门户中仅拥有订阅和租户级别的只读权限,但对特定资源组(RG)拥有完整的Contributor权限。Terraform脚本在plan阶段可以通过,但无论使用哪种脚本,apply阶段始终失败。
首次尝试(Sample 1)
代码:
terraform { required_providers { azurerm = { source = "hashicorp/azurerm" version = "=3.0.0" } } } provider "azurerm" { skip_provider_registration = true features {} } resource "azurerm_storage_account" "mytststorageacc" { name = "mytststorageacc" resource_group_name = "my_RG" location = "centralindia" account_tier = "Standard" account_replication_type = "LRS" }
报错信息:
Creating Azure Storage Account "mytststorageacc": storage.AccountsClient#Create: Failure sending request: StatusCode=403 -- Original Error: Code="AuthorizationFailed" Message="The client 'user@corp.com' with object id '' does not have authorization to perform action 'Microsoft.Storage/storageAccounts/write' over scope '/subscriptions//resourceGroups/my_RG/providers/Microsoft.Storage/storageAccounts/mytststorageacc' or the scope is invalid. If access was recently granted, please refresh your credentials.
第二次尝试(Sample 2)
代码:
terraform { required_providers { azurerm = { source = "hashicorp/azurerm" version = "=3.0.0" } } } provider "azurerm" { features {} client_id = "******************" client_secret = "******************" subscription_id = "******************" tenant_id = "******************" } resource "azurerm_storage_account" "mytststorageacc" { name = "mytststorageacc" resource_group_name = "my_RG" location = "centralindia" account_tier = "Standard" account_replication_type = "LRS" }
报错信息:
building account: getting authenticated object ID: listing Service Principals: ServicePrincipalsClient.BaseClient.Get(): clientCredentialsToken: received HTTP status 400 with response: {"error":"unauthorized_client","error_description":"AADSTS700016: Application with identifier '*****************' was not found in the directory 'az org'. This can happen if the application has not been installed by the administrator of the tenant or consented to by any user in the tenant. You may have sent your authentication request to the wrong tenant.
Trace ID: ***************************
Correlation ID: 0000000000000000000000000
Timestamp: 2023-09-13 06:14:59Z","error_codes":[700016],"timestamp":"2023-09-13 06:14:59Z","trace_id":"000000000000000000","correlation_id":"f000000000000000000","error_uri":"https://login.microsoftonline.com/error?code=700016"}
疑问
我原本预期上述代码能在拥有Contributor权限的资源组中创建存储账户,但却出现了如7000215、7000216这类错误。我已仔细核对了访问权限及client ID、密钥、租户ID、订阅ID等信息,理论上创建简单存储账户无需这些信息。
注:我可直接在Azure门户中创建所需资源。
请问:
- 使用Terraform在Azure中执行apply是否需要特殊权限?
- Terraform代码的运行机制是什么?
- 为何在拥有对应资源组权限的情况下仍出现400和403错误?
1. Terraform Azure执行apply的权限要求
Terraform Azurerm Provider执行资源操作时,除目标资源组的Contributor权限外,还可能涉及以下隐性要求:
- 资源提供程序注册验证:即使设置
skip_provider_registration = true,若订阅从未注册过对应资源提供程序(如Microsoft.Storage),底层API可能仍会检查订阅级的Microsoft.Resources/providers/write权限,但你能在门户创建资源,说明该提供程序已注册,可排除此点。 - 身份元数据读取权限:Terraform执行时需要读取部分租户/订阅级的身份元数据,若你的用户被租户级只读权限限制了这类元数据的访问,会导致权限检查失败。
2. Terraform代码的运行机制
Terraform核心分为三个执行阶段:
- Init:初始化工作目录,下载指定版本的Provider,配置状态存储后端。
- Plan:对比本地状态文件与Azure实际资源,生成执行计划。此阶段仅做只读查询,所以订阅/租户级只读权限即可完成。
- Apply:执行计划中的资源增删改操作。此阶段需要对目标资源及相关元数据拥有写入权限,Provider会通过Azure API发送修改请求,同时会做比门户操作更严格的身份验证与权限校验。
3. 403和400错误的原因分析
403错误(Sample 1)
- RBAC权限边界限制:租户/订阅级的只读权限可能设置了权限边界,限制了你在资源组级的实际操作范围,导致无法执行
Microsoft.Storage/storageAccounts/write。门户操作可能绕过了部分边界检查,而Terraform的API调用触发了严格校验。 - 凭据缓存过期:本地Azure CLI缓存的凭据未更新,导致Terraform使用旧的权限信息执行操作。
- 资源名称冲突:Azure返回的权限错误可能存在误导,实际是存储账户名称已被占用,可尝试更换名称重试。
400错误(Sample 2)
- 服务主体不存在:配置的client ID对应的应用注册未在指定tenant ID的租户中创建,或已被删除。
- 租户ID错误:核对tenant ID是否为你拥有资源组权限的租户。
- 服务主体未授权:即使服务主体存在,也需在目标资源组授予Contributor权限,否则身份验证通过后仍会出现403错误。
解决建议
针对Sample 1的修复:
- 刷新本地凭据:执行
az logout && az login,确保使用正确的用户账户。 - 验证权限有效性:运行
az role assignment list --assignee user@corp.com --resource-group my_RG,确认你确实拥有该资源组的Contributor权限。 - 检查权限边界:联系租户管理员确认你的用户是否被设置了权限边界,限制了资源组级写入操作。
- 刷新本地凭据:执行
针对Sample 2的修复:
- 确认服务主体存在:在Azure AD中搜索client ID对应的应用注册,确保其在指定租户中状态正常。
- 授予资源组权限:在目标资源组的访问控制中,为该服务主体添加Contributor角色。
- 核对配置信息:再次确认client_id、client_secret、tenant_id、subscription_id的准确性,避免复制错误。
内容的提问来源于stack exchange,提问作者Abhishek

