0

I have written a module called network_resources in which I am creating vnets and subnets attached to it. Below is the code for the module and how the module is being called:

main.tf

resource "azurerm_virtual_network" "vnets" {
  for_each            = var.vnets
  name                = each.key
  resource_group_name = var.resource_group_name
  location            = var.location
  address_space       = [each.value.address_space]
  dns_servers         = each.value.dns_servers
}

resource "azurerm_subnet" "subnets" {
  for_each             = local.subnets
  name                 = each.value.subnet_name
  resource_group_name  = var.resource_group_name
  virtual_network_name = azurerm_virtual_network.vnets[each.value.vnet_name].name
  address_prefixes     = [each.value.subnet_address]
  service_endpoints    = each.value.service_endpoints
}

local.tf

locals {
  subnets_flatlist = flatten([for key, val in var.vnets : [
    for subnet in val.subnets : {
      vnet_name         = key
      subnet_name       = subnet.subnet_name
      subnet_address    = subnet.subnet_address
      service_endpoints = subnet.service_endpoints
    }
    ]
  ])
  subnets = { for subnet in local.subnets_flatlist : subnet.subnet_address => subnet }
}

variables.tf

variable "resource_group_name" {
  description = "Name of the resource group to be imported."
  type        = string
}

variable "location" {
  description = "The location of the vnet to create. Defaults to the location of the resource group."
  type        = string
  default     = null
}

variable "vnets" {
  type = map(object({
    address_space = string
    dns_servers = list(string)
    subnets = list(object({
      subnet_name    = string
      subnet_address = string
      service_endpoints = list(string)
    }))
  }))
}

Code to call the module:

module "network_aks_prod1" {
  source                = "./network_resources_dns"
  vnets                 = var.vnets_aks_prod1
  resource_group_name = azurerm_resource_group.rg2.name
  location            = azurerm_resource_group.rg2.location
}

Variables.tf

vnets_aks_prod1 = {
    "bupaanz-mel-prod-caas-vnet01" = {
      address_space = "10.80.0.0/18"
      #dns_servers         = ["10.0.0.4" , "10.0.0.5"]
      dns_servers         = ["10.0.0.6","10.0.0.5", "10.0.0.6", "10.0.0.4","10.64.150.11"]
      subnets = [
        {
          subnet_name    = "subnet-mel-prod-aks-mgmt-10.80.9.64"
          subnet_address = "10.80.9.64/26"
          service_endpoints = []
        },
        {
          subnet_name    = "subnet-mel-prod-aks-internal1-10.80.0.0"
          subnet_address = "10.80.0.0/22"
          service_endpoints = []          
        },
        {
          subnet_name    = "GatewaySubnet"
          subnet_address = "10.80.9.0/26"
          service_endpoints = []         
        },  
        {
          subnet_name    = "subnet-mel-prod-aks-internal2-10.80.4.0"
          subnet_address = "10.80.4.0/22"
          service_endpoints = []     
        },     
        {
          subnet_name    = "subnet-mel-prod-aks-pa1-ext-10.80.8.0"
          subnet_address = "10.80.8.0/25"
          service_endpoints = []     
        },
        {
          subnet_name    = "subnet-mel-prod-aks-pa2-int-10.80.8.128"
          subnet_address = "10.80.8.128/25"
          service_endpoints = []     
        },
      ]
    },
}

All the vnets and subnets get created successfully with the above code. Now if I have to delete one subnet, i am removing the code for one of the subnet with all its attributes. Now when I run the terraform plan and apply again, it informs that one subnet will be deleted. It is showing in terraform apply also the subnet has got deleted but it is not getting deleted in the portal. Please can you let me know how the subnet will get deleted in the portal with the terraform code only using the code which I am using.

Output for terraform destroy of subnet

Aniket
  • 65
  • 7

1 Answers1

0
Update as of 18th May 2022

Following further triage, it appears that azurerm_virtual_network resource is being modified because of a change of tags/edge_zone/bgp_community/flow_timeout_in_minutes attributes.

While deleting a subnet, terraform plan shows it is going to modify above attributes but under the hoods, it is updating the azurerm_virtual_network resource from the state file which includes the subnet azurerm_subnet just deleted.

Workaround to get it working with azurerm_virtual_network is to add lifecycle block as shown below..

resource "azurerm_virtual_network" "vnets" {
  for_each            = var.vnets
  name                = each.key
  resource_group_name = var.resource_group_name
  location            = var.location
  address_space       = [each.value.address_space]
  dns_servers         = each.value.dns_servers

  lifecycle {
    ignore_changes = [
      # one or more of the below attributes
      tags, edge_zone, bgp_community, flow_timeout_in_minutes        
    ]
  }
}

TBH, I'm not sure of the side-effects of the above attributes by including them into lifecycle block but it doesn't update the vnet resources when these gets changed.

However this is a bug on Terraform provider, you must create an issue there to get this fixed.

You are unable to delete the subnet because you haven't set subnet = [] in the azurerm_virtual_network resource. Below is what the documentation here says::

Since subnet can be configured both inline and via the separate azurerm_subnet resource, we have to explicitly set it to empty slice ([]) to remove it.

Also, the doc says below::

Terraform currently provides both a standalone Subnet resource, and allows for Subnets to be defined in-line within the Virtual Network resource. At this time you cannot use a Virtual Network with in-line Subnets in conjunction with any Subnet resources. Doing so will cause a conflict of Subnet configurations and will overwrite subnets.

Nevertheless, applying an empty slice gave even worse results as it wiped off all subnets. I think it's better you use dynamic-blocks to create subnets as part of vnet resource rather than using subnet specific resource.

subnet {
    name           = "subnet-mel-prod-aks-pa1-ext-10.80.8.0"
    address_prefix = "10.80.8.0/25"
  }
  /*
  subnet {
    name           = "subnet-mel-prod-aks-pa2-int-10.80.8.128"
    address_prefix = "10.80.8.128/25"
  }
  */
  ...

From the above snippet, when I commented on the subnet, it did delete the only subnet I commented out. I'm not sure whether there is a bug to fix azurerm_subnet as its behavior is very weird.

harshavmb
  • 3,404
  • 3
  • 21
  • 55
  • so with the current code, there is no way we can delete one of the subnet and keep the rest – Aniket May 16 '22 at 02:49
  • So, I had sometime to look at this issue today. By any chance, your vnet resource (`azurerm_virtual_network`) has `tags` or `edge_zone` or `bgp_community` or `flow_timeout_in_minutes` changing when you reapply the subnet change? I did enable debug & these are the attributes which are propelling vnet to update from it's state file because of terraform sdk conflicts. And on update, vnet is restoring the subnets from the state file. – harshavmb May 18 '22 at 08:25
  • it is updating tags.. Is this the reason subnets are being restored back? If so is there any way to avoid it – Aniket May 20 '22 at 02:03
  • I’ve updated the answer. Can you try with `lifecycle` block? – harshavmb May 20 '22 at 05:09