Terraform v12 Dynamic Nested Block с несколькими полями содержимого?

У меня проблема с моим модулем для шлюза приложений Azure. Я преобразовал все в динамические блоки, и когда я вызываю свой модуль с вводом для 1 приложения, он отлично работает.

1 приложение:

module "my_appgw" {
  source = "../../../modules/module-application-gateway"
  location                    = var.location
  resource_group_name         = var.rsg
  subnet_id                   = "${data.azurerm_virtual_network.vnet_dmz.id}/subnets/waf"

  app = [
    {
      name                        = "app1-example.com"
      pick_host_name_from_backend = true,
    },
  ]
}

Однако, когда я добавляю второе приложение к своему вызову, я вижу проблемы.

2 приложения

module "my_appgw" {
  source = "../../../modules/module-application-gateway"
  location                    = var.location
  resource_group_name         = var.rsg
  subnet_id                   = "${data.azurerm_virtual_network.vnet_dmz.id}/subnets/waf"

  app = [
    {
      name                        = "app1-example.com"
      pick_host_name_from_backend = true,
    },
    {
      name                        = "app2-example.com"
    }
  ]
}

При добавлении нового приложения оно имеет как перенаправление 80, так и правило 443. Однако из-за индексации terraform хочет перезаписать мое первое правило приложения так, чтобы оба правила перенаправления 80 были первым и вторым в списке, а затем 443 правила третьим и четвертым в списке. Можем ли мы сделать несколько блоков контента в одном динамическом блоке? Как вы можете видеть, первое приложение меняется, когда я добавляю второе приложение в модуль, из-за чего ключи не совпадают (например, идентификатор app1 не соответствует ключу app2 и т. Д.). То же самое происходит с моим http_listener

Выходные изменения

request_routing_rule {

backend_address_pool_id = "<output omitted>/backendAddressPools/app1-example.com"

backend_address_pool_name = "app1-example.com"

backend_http_settings_id = "<output omitted>/backendHttpSettingsCollection/app1-example.com-https-settings"

backend_http_settings_name = "app1-example.com-https-settings"

http_listener_id = "<output omitted>/httpListeners/app1-example.com-443-https-list"

http_listener_name = "app1-example.com-443-https-list"

id = "<output omitted>/requestRoutingRules/app1-example.com-443"

name = "app1-example.com-443"

rule_type = "Basic"

}

~ request_routing_rule {

+ backend_address_pool_name = "app2-example.com"

+ backend_http_settings_name = "app2-example.com-https-settings"

http_listener_id = "<output omitted>/httpListeners/app1-example.com-http-list"

~ http_listener_name = "app1-example.com-http-list" -> "app2-example.com-443-https-list"

id = "<output omitted>/requestRoutingRules/app1-example.com-http-redirect"

~ name = "app1-example.com-http-redirect" -> "app2-example.com-443"

redirect_configuration_id = "<output omitted>/redirectConfigurations/app1-example.com-redirect"

- redirect_configuration_name = "app1-example.com-redirect" -> null

rule_type = "Basic"

}

+ request_routing_rule {

+ http_listener_name = "app1-example.com-http-list"

+ name = "app1-example.com-http-redirect"

+ redirect_configuration_name = "app1-example.com-redirect"

+ rule_type = "Basic"

}

+ request_routing_rule {

+ http_listener_name = "app2-example.com-http-list"

+ name = "app2-example.com-http-redirect"

+ redirect_configuration_name = "app2-example.com-redirect"

+ rule_type = "Basic"

}

Предлагаемая конфигурация, не работает

Несколько блоков содержимого на динамический блок

    for_each = [for i in var.app : {
      name = i.name
    }]
    content {
      name                           = "${http_listener.value.name}-${local.http_listener_name}"
      frontend_ip_configuration_name = local.frontend_ip_configuration_name
      frontend_port_name             = 80
      protocol                       = "Http"
    }
    content {
      name                           = "${http_listener.value.name}-${http_listener.value.port}-${local.https_listener_name}"
      frontend_ip_configuration_name = local.frontend_ip_configuration_name
      frontend_port_name             = http_listener.value.port
      ssl_certificate_name           = data.azurerm_key_vault_secret.cert_fe.name
      protocol                       = "Https"
    }
  }

Или вложенные динамические блоки в динамический блок, тоже не работает

dynamic "http_listener" {
    for_each = [for i in var.app : {
      name = i.name
    }]
    content {
      name                           = "${http_listener.value.name}-${local.http_listener_name}"
      frontend_ip_configuration_name = local.frontend_ip_configuration_name
      frontend_port_name             = 80
      protocol                       = "Http"

      dynamic "http_listener" {
        for_each = [for i in var.app : {
          name = i.name
          port = lookup(i, "frontend_port", 443) 
        }]
        content {
          name                           = "${http_listener.value.name}-${http_listener.value.port}-${local.https_listener_name}"
          frontend_ip_configuration_name = local.frontend_ip_configuration_name
          frontend_port_name             = http_listener.value.port
          ssl_certificate_name           = data.azurerm_key_vault_secret.cert_fe.name
          protocol                       = "Https"
        }
      }
    }

Кто-нибудь знает, как это исправить? Делает модуль шлюза приложений непригодным для использования, если существующие приложения будут изменяться и должны быть испорчены и воссозданы каждый раз при добавлении нового приложения.

1 ответ

Решение

В итоге я понял это. Вместо нескольких динамических вложенных блоков мне просто нужен был дополнительный цикл for, вложенный в начальный цикл for.

Вот связанный пример кода, который создает прослушиватель HTTP и HTTPS для каждого приложения. Он также создает правило HTTPS для внутреннего пула, а также правило перенаправления HTTP.

dynamic "http_listener" {
    for_each = flatten([
      for app in var.app: [
        for listener in app.listener : {
            fqdn    = app.fqdn
            protocol = listener.protocol
            port = listener.port
        }
      ]
    ])
    content {
        name                           = "%{ if http_listener.value.protocol == "https" }${http_listener.value.fqdn}-${http_listener.value.port}-${local.https_listener_name}%{ else }${http_listener.value.fqdn}-${local.http_listener_name}%{ endif }"  
        frontend_ip_configuration_name = local.frontend_ip_configuration_name
        frontend_port_name             = http_listener.value.port
        host_name                      = http_listener.value.fqdn
        protocol                       = upper(http_listener.value.protocol)
        ssl_certificate_name           = "%{ if http_listener.value.protocol == "https" }${data.azurerm_key_vault_secret.cert_fe.name}%{ endif }" 
      }
  }

dynamic "request_routing_rule" {
    for_each = flatten([
      for app in var.app: [
        for listener in app.listener : {
            fqdn    = app.fqdn
            protocol = listener.protocol
            port = listener.port
        }
      ]
    ])
    content {
        name                        = "%{ if request_routing_rule.value.protocol == "https" }${request_routing_rule.value.fqdn}-${request_routing_rule.value.port}%{ else }${request_routing_rule.value.fqdn}-redirect%{ endif }"  
        rule_type                   = "Basic"
        http_listener_name          = "%{ if request_routing_rule.value.protocol == "https" }${request_routing_rule.value.fqdn}-${request_routing_rule.value.port}-${local.https_listener_name}%{ else }${request_routing_rule.value.fqdn}-${local.http_listener_name}%{ endif }"
        backend_address_pool_name   = "%{ if request_routing_rule.value.protocol == "https" }${request_routing_rule.value.fqdn}%{ endif }" 
        backend_http_settings_name  = "%{ if request_routing_rule.value.protocol == "https" }${request_routing_rule.value.fqdn}-${local.backend_https_settings_name}%{ endif }" 
        redirect_configuration_name = request_routing_rule.value.protocol == "http" ? "${request_routing_rule.value.fqdn}-redirect" : null
    }

  }

  dynamic "redirect_configuration" {
    for_each = [for app in var.app : {
            fqdn    = app.fqdn
            port = app.listener.https.port

    }]
    content {
      name                 = "${redirect_configuration.value.fqdn}-redirect"
      redirect_type        = "Permanent"
      target_listener_name = "${redirect_configuration.value.fqdn}-${redirect_configuration.value.port}-${local.https_listener_name}"
    }

#... additional output omitted

А вот и вызов модуля. Вам необходимо указать прослушиватель http и https для приложения. Вы можете указать любой порт, в том числе нестандартный.

module "my_appgw" {
  source = "../../../modules/module-application-gateway"
  location                    = var.location
  resource_group_name         = var.rsg
  subnet_id                   = "${data.azurerm_virtual_network.vnet_dmz.id}/subnets/waf"
  app = {
    1 = {
      fqdn                        = "app1.com"
      pick_host_name_from_backend = true
      listener                    = {

         http = {
          protocol = "http"
          port = 80
        }
         https = {
          protocol = "https"
          port = 443
        }
      }

    }

  }


#... additional output omitted

}
Другие вопросы по тегам