azureterraformazure-application-gateway

terraform azure application gateway recreated when add new rule


i did a terraform module for manage azure app gateway. In variable.tf i set:

###### RULE apgw-01 ######
variable "rule_apgw-01" {
  type = map(object({
    backend_address_pool_name = string  # Name of the backend address pool
    http_setting_name         = string         # Name of the backend settings
    listener_name             = string                      # Name of the listener
    request_routing_rule_name = string          # Name of the rules
    frontend_port_name        = string         # Name of the frontend port
    frontend_port             = number
    priority                  = number
    port                      = number
    rule_type                 = string
    cookie_based_affinity     = string
    protocol                  = string
    host_name                 = string
    path                      = string
    fqdns                     = string  
  }))
  default = {
    "dv-apse-01" = {
      ### backend_address_pool      
      backend_address_pool_name = "dv-apse-01"
      fqdns                     = "dv-apse-01.azurewebsites.net"

      ### backend_http_settings
      http_setting_name         = "dv-apse-01.settings.http"
      protocol                  = "Http"
      port                      = 80      
      cookie_based_affinity     = "Disabled"
      path                      = ""

      ### listener
      listener_name             = "dv-apse-01.listener.http"
      host_name                 = "test.xyz.it"

      ### request_routing_rule
      request_routing_rule_name = "dv-apse-01.rule.http"
      rule_type                 = "Basic"
      frontend_port_name        = "port_80"
      frontend_port             = 80
      priority                  = 10
    }
  }
}

main.tf is configured as follow :

terraform {
  backend "azurerm" {}
  }


module "apgw-01" {
  source                         = "../../../../../modules/application_gateway"
  for_each                       = var.rule_apgw-01
  rg_name                        = data.terraform_remote_state.rg.outputs.rg-apgw
  name                           = "dv-apgw-01"
  skuName                        = "Standard_v2"
  skuTier                        = "Standard_v2"
  private_ip_address             = "xx.xx.xx.xx"
  public_ip_address_id           = data.terraform_remote_state.pbip.outputs.public_ip_pbip_apgw_01
  tag_division                   = var.tag_division
  tag_project                    = var.tag_project

  ### backend_address_pool
  backend_address_pool_name      = each.value.backend_address_pool_name
  fqdns = each.value.fqdns

  ### backend_http_settings  
  http_setting_name              = each.value.http_setting_name  
  protocol                       = each.value.protocol
  port                           = each.value.port
  cookie_based_affinity          = each.value.cookie_based_affinity  
  path                           = each.value.path

  ### listener
  listener_name                  = each.value.listener_name
  host_name                      = each.value.host_name

  ### request_routing_rule
  request_routing_rule_name      = each.value.request_routing_rule_name
  rule_type                      = each.value.rule_type
  frontend_port_name             = each.value.frontend_port_name
  frontend_port                  = each.value.frontend_port
  priority                       = each.value.priority
}

and call the module:

resource "azurerm_application_gateway" "network" {
  name                = var.name
  resource_group_name = var.rg_name
  location            = var.location
  zones               = ["1", "2", "3"]
  

  sku {
    name     = var.skuName
    tier     = var.skuTier
    capacity = local.capacity_apgw[substr(var.name, 0, 2)]
  }

  gateway_ip_configuration {
    name      = "appGatewayIpConfig"
    subnet_id = "${local.subnet_id_apgw[substr(var.name, 0, 2)]}"
  }

  frontend_port {
    name = var.frontend_port_name
    port = var.frontend_port
  }

  frontend_ip_configuration {  ### public frontend ip
    name                 = "appGwPublicFrontendIp"
    public_ip_address_id = var.public_ip_address_id
  }

 frontend_ip_configuration {   ### private frontend ip
    name                          = "appGwPrivateFrontendIp"
    subnet_id                     = "${local.subnet_id_apgw[substr(var.name, 0, 2)]}"
    private_ip_address_allocation = "Static"
    private_ip_address            = var.private_ip_address
  }

  backend_address_pool {
    name = var.backend_address_pool_name
    fqdns = [var.fqdns]
  }

  backend_http_settings {
    name                                = var.http_setting_name
    cookie_based_affinity               = var.cookie_based_affinity
    path                                = var.path
    port                                = var.port
    protocol                            = var.protocol
    request_timeout                     = var.request_timeout
    pick_host_name_from_backend_address = var.pick_host_name_from_backend_address
    probe_name                          = "dv-apse-we-01.probe.http"

    
  }

  http_listener {
    name                           = var.listener_name
    frontend_ip_configuration_name = "appGwPrivateFrontendIp"
    frontend_port_name             = var.frontend_port_name
    protocol                       = var.protocol
    host_name                      = var.host_name
  }

  request_routing_rule {
    name                       = var.request_routing_rule_name
    priority                   = var.priority
    rule_type                  = var.rule_type
    http_listener_name         = var.listener_name
    backend_address_pool_name  = var.backend_address_pool_name
    backend_http_settings_name = var.http_setting_name
  }

  probe {
    name                = "dv-apse-we-01.probe.http"
    protocol            = "Http"
    pick_host_name_from_backend_http_settings = true
    path                = "/"
    interval            = 30
    timeout             = 30
    unhealthy_threshold = 3
    
  match {
   status_code = ["200-399"]
  } 

  }

  probe {
    name                = "dv-apse-we-01.probe.https"
    protocol            = "Https"
    pick_host_name_from_backend_http_settings = true
    path                = "/"
    interval            = 30
    timeout             = 30
    unhealthy_threshold = 3

  match {
   status_code = ["200-399"]
  } 

  }



  tags = {
    division     = var.tag_division
    project      = var.tag_project
    resourceType = var.tag_resourceType
    referent     = var.tag_referent
  }

  lifecycle {
    ignore_changes = [tags]
    
  }  

}

code is working fine when i execute it the first time, it will create correctly the resource declared. The problem start when i try to add a new block in variable.tf:

  default = {
    "app01" = {
      ### backend_address_pool      
      backend_address_pool_name = "app01"
      fqdns                     = "app01.azurewebsites.net"

      ### backend_http_settings
      http_setting_name         = "app01.settings.http"
      protocol                  = "Http"
      port                      = 80      
      cookie_based_affinity     = "Disabled"
      path                      = ""

      ### listener
      listener_name             = "app01.listener.http"
      host_name                 = "test.xyz.it"

      ### request_routing_rule
      request_routing_rule_name = "app01.rule.http"
      rule_type                 = "Basic"
      frontend_port_name        = "port_80"
      frontend_port             = 80
      priority                  = 10
    }
    "app02" = {
      ### backend_address_pool      
      backend_address_pool_name = "app02"
      fqdns                     = "app02.azurewebsites.net"

      ### backend_http_settings
      http_setting_name         = "app02.settings.http"
      protocol                  = "Http"
      port                      = 80      
      cookie_based_affinity     = "Disabled"
      path                      = ""

      ### listener
      listener_name             = "app02.listener.http"
      host_name                 = "test2.xyz.it"

      ### request_routing_rule
      request_routing_rule_name = "app02.rule.http"
      rule_type                 = "Basic"
      frontend_port_name        = "port_80"
      frontend_port             = 80
      priority                  = 10
    }
  }

when i execute plan, terraform destroy app gateway and recreate it. Why doesn't terraform recognize that app gateway already exists and just add the new rules? I terraform plan i also see that with the two block it take the 02.

Plan output:

module.apgw-apse-01["dv-apse-02"].azurerm_application_gateway.network: Creating...Error: A resource with the ID"dv-apgw-01" already exists - to be managed via Terraform this resource needs to be imported into the State. Please see the resource documentation for "azurerm_application_gateway" for more information. with module.apgw-apse-01["dv-apse-02"].azurerm_application_gateway.network, on ..........\modules\application_gateway\main.tf line 1, in resource "azurerm_application_gateway" "network": 1: resource "azurerm_application_gateway" "network" {


Solution

  • as written before, i solved using dynamic block:

    variable "backend_pools" {
      description = "Map of backend pools for the application gateway"
      type = list(object({
        backend_address_pool_name = string         # Name of the backend pool
        http_setting_name         = string         # Name of the backend settings
        listener_name             = string                      # Name of the listener
        request_routing_rule_name = string          # Name of the rules
        frontend_port_name        = string         # Name of the frontend port
        frontend_port             = number
        priority                  = number
        port                      = number
        rule_type                 = string
        cookie_based_affinity     = string
        protocol                  = string
        host_name                 = string
        path                      = string
        fqdns                     = string  
      }))
      default = [
        {
          ### backend_address_pool      
          backend_address_pool_name = "dv-apse-01"
          fqdns                     = "dv-apse-01.azurewebsites.net"
    
          ### backend_http_settings
          http_setting_name         = "dv-apse-01.settings.http"
          protocol                  = "Http"
          port                      = 80      
          cookie_based_affinity     = "Disabled"
          path                      = ""
    
          ### listener
          listener_name             = "dv-apse-01.listener.http"
          host_name                 = "app.aaa.it"
    
          ### request_routing_rule
          request_routing_rule_name = "dv-apse-01.rule.http"
          rule_type                 = "Basic"
          frontend_port_name        = "port_80"
          frontend_port             = 80
          priority                  = 10
        },
        {
          ### backend_address_pool      
          backend_address_pool_name = "dv-apse-02"
          fqdns                     = "dv-apse-02.azurewebsites.net"
    
          ### backend_http_settings
          http_setting_name         = "dv-apse-02.settings.http"
          protocol                  = "Http"
          port                      = 80      
          cookie_based_affinity     = "Disabled"
          path                      = ""
    
          ### listener
          listener_name             = "dv-apse-02.listener.http"
          host_name                 = "aaaa.aaa.it"
    
          ### request_routing_rule
          request_routing_rule_name = "dv-apse-02.rule.http"
          rule_type                 = "Basic"
          frontend_port_name        = "port_80"
          frontend_port             = 80
          priority                  = 20
        },
        {
          ### backend_address_pool      
          backend_address_pool_name = "dv-apse-03"
          fqdns                     = "dv-apse-03.azurewebsites.net"
    
          ### backend_http_settings
          http_setting_name         = "dv-apse-03.settings.http"
          protocol                  = "Http"
          port                      = 80      
          cookie_based_affinity     = "Disabled"
          path                      = ""
    
          ### listener
          listener_name             = "dv-apse-03.listener.http"
          host_name                 = "asdad.paa.it"
    
          ### request_routing_rule
          request_routing_rule_name = "dv-apse-03.rule.http"
          rule_type                 = "Basic"
          frontend_port_name        = "port_80"
          frontend_port             = 80
          priority                  = 30
        }
      ]
    }
    

    and main now is like:

    resource "azurerm_application_gateway" "network" {
      name                = var.name
      resource_group_name = var.rg_name
      location            = var.location
      zones               = ["1", "2", "3"]
      
      sku {
        name     = var.skuName
        tier     = var.skuTier
        capacity = local.capacity_apgw[substr(var.name, 0, 2)]
      }
    
      gateway_ip_configuration {
        name      = "appGatewayIpConfig"
        subnet_id = "${local.subnet_id_apgw[substr(var.name, 0, 2)]}"
      }
    
      frontend_ip_configuration {  ### public frontend ip
        name                 = "appGwPublicFrontendIp"
        public_ip_address_id = var.public_ip_address_id
      }
    
     frontend_ip_configuration {   ### private frontend ip
        name                          = "appGwPrivateFrontendIp"
        subnet_id                     = "${local.subnet_id_apgw[substr(var.name, 0, 2)]}"
        private_ip_address_allocation = "Static"
        private_ip_address            = var.private_ip_address
      }
    
    
    dynamic frontend_port {
      for_each = var.backend_pools
      content {
        name = frontend_port.value.frontend_port_name
        port = frontend_port.value.frontend_port
      }
    }
    
    dynamic backend_address_pool {
      for_each = var.backend_pools
      content {
        name  = backend_address_pool.value.backend_address_pool_name
        fqdns = [backend_address_pool.value.fqdns]
      }
    }
    
    dynamic backend_http_settings {
        for_each = var.backend_pools
          content {
        name                                = backend_http_settings.value.http_setting_name
        cookie_based_affinity               = backend_http_settings.value.cookie_based_affinity
        path                                = backend_http_settings.value.path
        port                                = backend_http_settings.value.port
        protocol                            = backend_http_settings.value.protocol
        request_timeout                     = var.request_timeout
        pick_host_name_from_backend_address = var.pick_host_name_from_backend_address
        probe_name                          = "dv-apse-we-01.probe.http"
    
    }
      }
    
      dynamic http_listener {
            for_each = var.backend_pools
          content {
        name                           = http_listener.value.listener_name
        frontend_ip_configuration_name = "appGwPrivateFrontendIp"
        frontend_port_name             = http_listener.value.frontend_port_name
        protocol                       = http_listener.value.protocol
        host_name                      = http_listener.value.host_name
      }
      }
    
     dynamic request_routing_rule {
            for_each = var.backend_pools
          content {
        name                       = request_routing_rule.value.request_routing_rule_name
        priority                   = request_routing_rule.value.priority
        rule_type                  = request_routing_rule.value.rule_type
        http_listener_name         = request_routing_rule.value.listener_name
        backend_address_pool_name  = request_routing_rule.value.backend_address_pool_name
        backend_http_settings_name = request_routing_rule.value.http_setting_name
      }
      }
    
      probe {
        name                = "dv-apse-we-01.probe.http"
        protocol            = "Http"
        pick_host_name_from_backend_http_settings = true
        path                = "/"
        interval            = 30
        timeout             = 30
        unhealthy_threshold = 3
        
      match {
       status_code = ["200-399"]
      } 
    
      }
    
      probe {
        name                = "dv-apse-we-01.probe.https"
        protocol            = "Https"
        pick_host_name_from_backend_http_settings = true
        path                = "/"
        interval            = 30
        timeout             = 30
        unhealthy_threshold = 3
    
      match {
       status_code = ["200-399"]
      } 
    
      }
    
    
    
      tags = {
        division     = var.tag_division
        project      = var.tag_project
        resourceType = var.tag_resourceType
        referent     = var.tag_referent
      }
    
      lifecycle {
        ignore_changes = [tags]
        
      }  
    
    }