如何通过单个Terraform项目批量创建Azure订阅并在其中部署vNet?
这确实是Terraform管理多Azure订阅时的典型痛点——毕竟Provider配置本质上是静态的,但咱们可以通过分阶段执行+动态生成Provider配置的方式来解决,而且完全适配你几百个订阅的场景。下面是具体的可行方案和最佳实践:
核心思路:拆分执行阶段 + 动态生成Provider配置
因为新订阅的ID和SP凭据是运行时生成的,没法预先写死Provider配置,所以我们把整个流程拆成两个独立的Terraform执行阶段,用脚本衔接动态生成Provider配置的环节。
阶段1:批量创建订阅与服务主体
首先编写专门的Terraform代码,只负责创建订阅、SP并分配权限,最后输出所有订阅的关键信息(ID、SP客户端ID、客户端密钥、租户ID等)。
示例代码片段:
provider "azurerm" { features {} # 用拥有创建订阅权限的主账号/SP配置 subscription_id = var.central_management_subscription_id } # 批量创建订阅 resource "azurerm_subscription" "customer_subs" { for_each = var.customer_subscription_list alias = each.value.alias billing_scope_id = var.billing_scope_id } # 为每个订阅创建SP resource "azurerm_azuread_service_principal" "customer_sps" { for_each = azurerm_subscription.customer_subs application_id = azurerm_azuread_application.customer_apps[each.key].application_id } # 输出所有订阅和SP信息,供下一阶段使用 output "subscriptions" { value = { for k, v in azurerm_subscription.customer_subs : k => { id = v.id client_id = azurerm_azuread_service_principal.customer_sps[k].application_id client_secret= azurerm_azuread_application_password.customer_sp_passwords[k].value tenant_id = data.azurerm_client_config.current.tenant_id } } sensitive = true }
运行这个阶段后,把输出导出为JSON文件:
terraform output -json subscriptions > subscriptions_output.json
阶段2:动态生成Provider配置 + 创建vNet与vWAN连接
这一步我们用脚本读取上一阶段的输出,动态生成包含所有订阅Provider别名的配置文件,再用Terraform批量创建每个订阅内的vNet并连接到中央vWAN。
步骤1:用脚本生成Provider配置
比如用Python脚本generate_providers.py读取subscriptions_output.json,生成providers.tf.json:
import json with open("subscriptions_output.json", "r") as f: subs = json.load(f) providers = { "provider": { "azurerm": [] } } for sub_name, sub_data in subs.items(): providers["provider"]["azurerm"].append({ "alias": sub_name, "subscription_id": sub_data["id"], "client_id": sub_data["client_id"], "client_secret": sub_data["client_secret"], "tenant_id": sub_data["tenant_id"], "features": {} }) with open("providers.tf.json", "w") as f: json.dump(providers, f, indent=2)
运行脚本后,会生成包含所有订阅Provider别名的JSON配置文件。
步骤2:编写vNet与vWAN连接的Terraform代码
# 中央vWAN所在的默认Provider(管理中央基础设施的订阅) provider "azurerm" { features {} subscription_id = var.central_management_subscription_id } # 读取订阅列表 locals { subscriptions = jsondecode(file("subscriptions_output.json")) } # 为每个订阅创建资源组 resource "azurerm_resource_group" "customer_rg" { for_each = local.subscriptions provider = azurerm[each.key] name = "${each.key}-rg" location = var.resource_location } # 为每个订阅创建vNet resource "azurerm_virtual_network" "customer_vnet" { for_each = local.subscriptions provider = azurerm[each.key] name = "${each.key}-vnet" address_space = ["10.${each.value.index}.0.0/16"] location = var.resource_location resource_group_name = azurerm_resource_group.customer_rg[each.key].name } # 将vNet连接到中央vWAN resource "azurerm_virtual_wan_connection" "vnet_connection" { for_each = local.subscriptions name = "${each.key}-vwan-link" virtual_wan_id = azurerm_virtual_wan.central_vwan.id virtual_hub_id = azurerm_virtual_hub.central_hub.id remote_virtual_network_id = azurerm_virtual_network.customer_vnet[each.key].id allow_remote_virtual_network_traffic = true }
最后运行这个阶段的Terraform代码,就能批量完成所有订阅的vNet创建和vWAN连接。
额外注意事项
- 权限控制:确保每个订阅的SP拥有足够权限(比如
Contributor)在订阅内创建资源。 - 状态管理:建议把两个阶段的Terraform状态分开存储(比如用不同的存储容器),避免互相干扰。
- 错误处理:脚本要加入异常处理逻辑,跳过创建失败的订阅,避免影响整体批量执行。
内容的提问来源于stack exchange,提问作者Daniel
相关产品推荐
相关产品推荐

