Terraform中如何配置可选的azurerm与azapi多订阅Provider?
我有订阅专属的azurerm和azapi Provider配置,示例如下:
provider "azurerm" { alias = "first" tenant_id = "<>" subscription_id = var.subscription_id skip_provider_registration = true use_oidc = true features {} } provider "azapi" { alias = "first-api" tenant_id = "<>" subscription_id = var.subscription_id use_oidc = true }
我通过terraform配置文件切换subscription_id,希望在同一配置中仅在部分环境(比如prod.tfvars有值、dev.tfvars为空)跨多订阅部署资源。但当把subscription_id设为空值时,会出现如下报错:
Error: building account: unable to configure ResourceManagerAccount: subscription ID could not be determined and was not specified with provider["registry.terraform.io/hashicorp/azurerm"].second, on provider.tf line 81, in provider "azurerm": 81: provider "azurerm" {
我考虑过把使用这些Provider的部分拆分到独立模块,但根据Terraform官方文档,所有Provider都是全局的,这个方案不可行。想请教:是否可以配置可选Provider仅在需要时加载?或是我对文档的理解有误?
1. 用count/for_each实现条件化Provider实例
Terraform支持对Provider配置使用count或for_each控制实例是否创建,以此实现仅当subscription_id有值时才加载对应Provider。
以azurerm为例,使用count的配置方式:
provider "azurerm" { count = var.subscription_id != "" ? 1 : 0 alias = "second" tenant_id = "<>" subscription_id = var.subscription_id skip_provider_registration = true use_oidc = true features {} }
当var.subscription_id为空时,该Provider实例不会被创建,也就不会触发配置报错。引用时需通过索引(如azurerm.second[0])。
如果觉得索引不够直观,也可以用for_each:
provider "azurerm" { for_each = var.subscription_id != "" ? toset([var.subscription_id]) : toset([]) alias = "second" tenant_id = "<>" subscription_id = each.key skip_provider_registration = true use_oidc = true features {} }
引用时直接使用azurerm.second[var.subscription_id]即可。
2. 纠正模块与Provider的理解偏差
你之前对官方文档的理解有误:模块可以声明自身的Provider依赖,并且通过providers参数传递不同的Provider实例,实现不同模块使用不同订阅的Provider。
比如先创建一个跨订阅资源模块:
# ./modules/cross-subscription/main.tf resource "azurerm_resource_group" "example" { provider = azurerm.second name = "example-rg" location = "East US" }
在根模块调用时,仅当var.subscription_id有值时才启用该模块并传递对应Provider:
module "cross_subscription_resources" { source = "./modules/cross-subscription" count = var.subscription_id != "" ? 1 : 0 providers = { azurerm.second = azurerm.second[0] } }
这样模块只会在需要跨订阅时被创建,对应的Provider也仅在此时加载。
3. 额外优化:默认Provider+条件Provider结合
可以保留一个默认Provider用于主订阅资源部署,再用条件Provider处理跨订阅场景,主环境资源不受影响,仅在需要时启用跨订阅的Provider实例。
内容的提问来源于stack exchange,提问作者simpleCoder

