You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过单个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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 11:24:10