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

如何避免Terraform误删Azure虚拟网络中新增的子网资源

解决Terraform重复执行时删除Databricks新增子网的问题

问题描述

我有两个Terraform脚本:一个用于创建带虚拟网络和1个子网的Azure VM;另一个用于创建Databricks,并在该Azure VM的虚拟网络中添加2个子网,以实现Databricks与VM的连通。首次执行terraform apply后一切正常,但再次执行时,Terraform试图删除这2个新增子网。

VM创建网络及子网的代码

resource "azurerm_virtual_network" "neo4j_virt_network" {
  name                = "neo4j-virtnetwork-${var.env}-${var.location}-001"
  address_space       = ["10.0.0.0/16"]
  location            = var.location
  resource_group_name = var.resource_group

  subnet {
    name           = "neo4j-virtnetwork-subnet-${var.env}-${var.location}-001"
    address_prefix = "10.0.1.0/24"
    security_group = azurerm_network_security_group.neo4j_sg.id
  }

  tags = local.tags
}

Databricks创建2个新增子网的代码

resource "azurerm_subnet" "private" {
  name = "databricks-dev-northeurope-001-private"
  resource_group_name  = var.resource_group
  virtual_network_name = azurerm_virtual_network.neo4j_virt_network.name
  address_prefixes     = ["10.0.3.0/24"]

  delegation {
    name = "databricks-delegation"

    service_delegation {
      name = "Microsoft.Databricks/workspaces"
      actions = [
        "Microsoft.Network/virtualNetworks/subnets/join/action",
        "Microsoft.Network/virtualNetworks/subnets/prepareNetworkPolicies/action",
        "Microsoft.Network/virtualNetworks/subnets/unprepareNetworkPolicies/action",
      ]
    }
  }
}

resource "azurerm_network_security_group" "private" {
  name = "databricks-dev-northeurope-001-private-sg"
  resource_group_name = var.resource_group
  location            = var.location
}

resource "azurerm_subnet_network_security_group_association" "private" {
  subnet_id                 = azurerm_subnet.private.id
  network_security_group_id = azurerm_network_security_group.private.id
}

resource "azurerm_subnet" "public" {
  name = "databricks-dev-northeurope-001-public"
  resource_group_name  = var.resource_group
  virtual_network_name = azurerm_virtual_network.neo4j_virt_network.name
  address_prefixes     = ["10.0.5.0/24"]

  delegation {
    name = "databricks-delegation"

    service_delegation {
      name = "Microsoft.Databricks/workspaces"
      actions = [
        "Microsoft.Network/virtualNetworks/subnets/join/action",
        "Microsoft.Network/virtualNetworks/subnets/prepareNetworkPolicies/action",
        "Microsoft.Network/virtualNetworks/subnets/unprepareNetworkPolicies/action",
      ]
    }
  }
}

resource "azurerm_network_security_group" "public" {
  name = "databricks-dev-northeurope-001-public-sg"
  resource_group_name = var.resource_group
  location            = var.location
}

resource "azurerm_subnet_network_security_group_association" "public" {
  subnet_id                 = azurerm_subnet.public.id
  network_security_group_id = azurerm_network_security_group.public.id
}

resource "azurerm_databricks_workspace" "databricks" {
  name                 = "databricks-${var.env}-${var.location}-001"
  resource_group_name  = var.resource_group
  location             = var.location
  sku                  = "standard"

  managed_resource_group_name = "${var.company_name}-rg-databricks-workspace-${var.env}-${var.location}-001"

  custom_parameters {
    machine_learning_workspace_id                        = azurerm_machine_learning_workspace.ml_workspace.id
    storage_account_name                                 = "databrick${var.env}${random_string.db_code.result}"
    virtual_network_id                                   = azurerm_virtual_network.neo4j_virt_network.id
    public_subnet_name                                   = azurerm_subnet.public.name
    public_subnet_network_security_group_association_id  = azurerm_subnet_network_security_group_association.public.id
    private_subnet_name                                  = azurerm_subnet.private.name
    private_subnet_network_security_group_association_id = azurerm_subnet_network_security_group_association.private.id
  }

  depends_on = [
    azurerm_subnet_network_security_group_association.public,
    azurerm_subnet_network_security_group_association.private,
  ]

  tags = local.tags
}

调试日志

2022-11-10T09:28:12.050+0200 [WARN]  Provider "registry.terraform.io/hashicorp/azurerm" produced an unexpected new value for module.dev.azurerm_virtual_network.neo4j_virt_network during refresh.
      - .subnet: actual set element cty.ObjectVal(map[string]cty.Value{"address_prefix":cty.StringVal("10.0.3.0/24"), "id":cty.StringVal("[...]"), "name":cty.StringVal("databricks-dev-northeurope-001-private"), "security_group":cty.StringVal("[...]")) does not correlate with any element in plan
      - .subnet: actual set element cty.ObjectVal(map[string]cty.Value{"address_prefix":cty.StringVal("10.0.5.0/24"), "id":cty.StringVal("[...]"), "name":cty.StringVal("databricks-dev-northeurope-001-public"), "security_group":cty.StringVal("[...]")) does not correlate with any element in plan
      - .subnet: length changed from 1 to 3

问题原因

核心矛盾在于Terraform对Azure虚拟网络子网的声明式管理逻辑:

  • 你在azurerm_virtual_network.neo4j_virt_network资源内通过嵌套的subnet块定义了VM子网,Terraform会将这个嵌套块视为该虚拟网络下所有子网的完整清单。
  • 当通过独立的azurerm_subnet资源添加Databricks子网后,Terraform刷新状态时发现实际子网数量(3个)与代码定义的嵌套子网数量(1个)不匹配,因此会试图删除额外的两个子网,以让资源状态与代码定义一致。

解决方案

方法1:统一使用独立子网资源声明

将虚拟网络内的嵌套子网块移除,改用独立的azurerm_subnet资源定义VM子网,与Databricks子网保持一致的声明方式:

resource "azurerm_virtual_network" "neo4j_virt_network" {
  name                = "neo4j-virtnetwork-${var.env}-${var.location}-001"
  address_space       = ["10.0.0.0/16"]
  location            = var.location
  resource_group_name = var.resource_group

  tags = local.tags
}

# 原嵌套子网改为独立资源
resource "azurerm_subnet" "neo4j" {
  name                 = "neo4j-virtnetwork-subnet-${var.env}-${var.location}-001"
  resource_group_name  = var.resource_group
  virtual_network_name = azurerm_virtual_network.neo4j_virt_network.name
  address_prefixes     = ["10.0.1.0/24"]

  security_group = azurerm_network_security_group.neo4j_sg.id
}

此方法让所有子网都通过独立资源管理,Terraform会分别跟踪每个子网的状态,不会再试图删除Databricks的子网。

方法2:忽略虚拟网络的子网变化

如果不想修改原有嵌套子网的声明方式,可在虚拟网络资源中添加lifecycle块,让Terraform忽略子网属性的变化:

resource "azurerm_virtual_network" "neo4j_virt_network" {
  name                = "neo4j-virtnetwork-${var.env}-${var.location}-001"
  address_space       = ["10.0.0.0/16"]
  location            = var.location
  resource_group_name = var.resource_group

  subnet {
    name           = "neo4j-virtnetwork-subnet-${var.env}-${var.location}-001"
    address_prefix = "10.0.1.0/24"
    security_group = azurerm_network_security_group.neo4j_sg.id
  }

  tags = local.tags

  lifecycle {
    ignore_changes = [subnet]
  }
}

该方法会让Terraform不再检查虚拟网络的子网列表是否与代码一致,从而保留新增的Databricks子网。但注意:后续若需要修改原VM子网的配置,需手动处理,因为Terraform会忽略该属性的所有变化。


验证步骤

修改代码后,执行terraform plan检查是否还会出现删除子网的计划,确认无误后执行terraform apply即可。

内容的提问来源于stack exchange,提问作者Vasyl Stepulo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 03:35:21