Terraform同模块中循环创建Azure订阅与资源组的实现方案
在同一Terraform模块中为多个动态创建的Azure订阅添加资源组
问题背景
我有一个Terraform模块,需要先创建Azure订阅,再为每个订阅添加对应的资源组及其他资源。但azurerm provider在Terraform初始化阶段就已完成配置,这一阶段早于订阅资源的生成,无法动态创建provider实例,也没法通过循环更新provider配置中的subscription_id参数。这种场景下,如何在同一模块中为多个订阅分别创建对应的资源组?
用户提供的初始代码示例:
locals { subscriptions = ["example1", "example2"] resource_groups = ["rg1", "rg2"] } resource "azurerm_subscription" "example" { for_each = local.subscriptions subscription_name = each.value.name } resource "azurerm_resource_group" "example" { for_each = local.resource_groups name = each.value.name location = "West Europe" }
解决方案:嵌套模块+实例化专属Provider
利用Terraform的模块嵌套特性,将单个订阅的资源创建逻辑封装为子模块,通过for_each遍历已创建的订阅,为每个订阅实例化独立的子模块,并在子模块中配置指向该订阅的azurerm provider。
步骤1:创建子模块
新建子模块目录(例如./modules/subscription_resources),子模块负责接收订阅ID,配置专属Provider并创建对应资源组。
子模块main.tf代码:
variable "subscription_id" { type = string description = "目标Azure订阅的ID" } variable "resource_groups" { type = list(object({ name = string location = string })) description = "要在该订阅中创建的资源组列表" } # 配置指向当前订阅的Provider provider "azurerm" { alias = "target_sub" subscription_id = var.subscription_id # 认证信息可继承父模块的环境变量/CLI登录,无需重复配置 } # 为当前订阅创建多个资源组 resource "azurerm_resource_group" "rg" { provider = azurerm.target_sub for_each = { for rg in var.resource_groups : rg.name => rg } name = each.value.name location = each.value.location }
步骤2:修改父模块逻辑
在父模块中先创建订阅,再调用子模块为每个订阅分配资源组:
父模块main.tf代码:
locals { # 定义订阅与对应资源组的映射关系 subscription_configs = { example1 = { sub_name = "业务订阅1" resource_groups = [ { name = "rg-business-1", location = "West Europe" }, { name = "rg-dev-1", location = "East Asia" } ] }, example2 = { sub_name = "业务订阅2" resource_groups = [ { name = "rg-business-2", location = "West Europe" } ] } } } # 批量创建Azure订阅 resource "azurerm_subscription" "main" { for_each = local.subscription_configs subscription_name = each.value.sub_name } # 为每个订阅实例化子模块,创建对应资源组 module "sub_resource_groups" { for_each = azurerm_subscription.main source = "./modules/subscription_resources" subscription_id = each.value.id resource_groups = local.subscription_configs[each.key].resource_groups }
方案原理
- Terraform的模块实例化发生在资源创建阶段之后,因此子模块可以安全引用父模块中已创建的订阅ID。
- 每个子模块实例会配置独立的azurerm provider(通过
alias区分),指向对应的订阅,实现资源与订阅的一一绑定。 - 认证逻辑可复用父模块的配置(如环境变量
AZURE_CLIENT_ID、AZURE_TENANT_ID等),无需重复设置。
注意事项
- 父模块的azurerm provider需要具备创建Azure订阅的权限(通常需关联租户或管理组级别的角色)。
- 若需为单个订阅创建多个资源组,只需在
subscription_configs中扩展对应资源组列表即可,子模块会自动遍历创建。
内容的提问来源于stack exchange,提问作者Mark
相关产品推荐
相关产品推荐

