具有多个内容字段的 Terraform v12 动态嵌套块?

Terraform v12 Dynamic Nested Block with multiple content fields?

我 运行 我的 Azure App Gateway 模块出现问题。我已将所有内容都转换为动态块,当我使用 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 id 与 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"
        }
      }
    }

有人知道如何解决这个问题吗?如果现有应用程序将更改并且每次添加新应用程序时都必须被污染和重新创建,则使应用程序网关模块不可用。

我终于搞清楚了。我只需要在初始 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

}