如何使用 terraform 动态块在 az 负载均衡器中构建多个前端 ip,该负载均衡器利用 public ips 用于 azure 可用性区域

How to use terraform dynamic block to build multiple front end ip in the az load balancer that leverages public ips for azure availability zones

我 运行 遇到的问题与负载均衡器前端 ip 有关,因此它可以使用唯一 Public IP 访问不同可用区中的不同服务器。我在创建 Public IP 时使用计数,但我没有在负载平​​衡器上使用计数,因为我不想为每个服务器使用新的 LB。如果我能以某种方式将 Public IP 保存到一个变量,那么我可以在动态块中使用 for_each 引用它们,但我找不到这样做的方法。这是我到目前为止的代码,但它不能按原样工作。这个问题可能没有办法解决,真的很臭。 顺便说一句,我正在使用下面的拆分函数,所以它 returns 是属性需要的列表。这有点老套,但确实有效。

resource "azurerm_public_ip" "pip" {
  count               = "${var.nblinuxvms}"
  name                = "${var.proj_name}-lbpip${count.index}-${var.region}-${var.app_env}"
  location            =  var.region
  resource_group_name = "${azurerm_resource_group.rg.name}"
  allocation_method   = "Static"   #Public IP Standard SKUs require allocation_method to be set to Static
  sku                 = "Standard" #Standard SKU Required for Zones
  domain_name_label   = "${var.proj_name}${count.index}${split("", "${element(["1", "2", "3"], "${count.index}")}")}"
  zones = "${var.avzones}" ? split("", "${element(["1", "2", "3"], "${count.index}")}") : null
}
resource "azurerm_lb" "lb" {
  name                = "externallb"
  location            = "${azurerm_resource_group.rg.location}"
  resource_group_name = "${azurerm_resource_group.rg.name}"
  sku                 = "standard" #standard SKU needed to support zones
 dynamic "frontend_ip_configuration" {
   for_each             = "${azurerm_public_ip.test.*.ip_address}" #this is the problem line. I need a way to store all the IPs in a variable and then iterate through them for each new frontend ip configuration
   content{
     name  = "primary${count.index}" #This name is also important as this is how I'll connect the nat rule down below
     public_ip_address_id = "${azurerm_public_ip.pip.id}"
  }
resource "azurerm_lb_nat_rule" "lbnr" {
  count                          = "${var.nblinuxvms}"
  resource_group_name            = "${azurerm_resource_group.rg.name}"
  loadbalancer_id                = "${azurerm_lb.lb.id}"
  name                           = "SSHHost${count.index}"
  protocol                       = "Tcp"
  frontend_port                  = "${2200 + count.index}"
  backend_port                   = 22
  frontend_ip_configuration_name = "primary${count.index}" #This name needs to match the LB Front End IP Configuartion
}

frontend_ip_configuration_name 需要匹配负载均衡器名称。带有 for_each 的动态块似乎是特定问题的最佳解决方案,因为它不是资源……但我看不到将 public ip 保存到我可以引用的任何变量的方法。如果没有解决方案,人们如何解决这个问题?通过为每个 Azure 可用性区域创建一个单独的 LB?因为它必须是标准的,而不是看起来成本过高的基本 LB。希望我只是错过了一些东西。任何帮助将不胜感激。请注意,我只分享了我的 Terraform 项目中的相关代码。如果需要更多代码,请告诉我。(我无法将动态块添加到问题标签,因为我的代表太低了。) 谢谢, -山姆·卡查尔

我觉得你正在尝试做的事情非常有意义并且应该是可能的。一些注意事项:

  • for_each 块中,您可以使用 each.value 访问值(在本例中是 public ip 对象)。详情见for_each documentation
  • ip_address指的是实际分配的ip,不是对象
  • countfor_each 块中不可用,因此 frontend_ip_configuration 块的名称应直接从 public ip 对象推导。

鉴于以上情况,您可以尝试这样的操作(未测试!):

resource "azurerm_lb_nat_rule" "lbnr" {
  count                          = "${var.nblinuxvms}"
  ...
  frontend_ip_configuration_name = "config_${azurerm_public_ip[count].name}"
}
dynamic "frontend_ip_configuration" {
   for_each = "${azurerm_public_ip.pip}"
   content{
     name                 = "config_${each.value.name}" 
     public_ip_address_id = "${each.value.id}"
  }

我假设您使用的是 terraform 0.12,因为 for_each 在 0.11 中不可用。您正在使用的冗长插值语法已在最新版本中弃用,最好使用新的:

resource "azurerm_lb_nat_rule" "lbnr" {
  count                          = var.nblinuxvms
  ...
  frontend_ip_configuration_name = "config_${azurerm_public_ip[count].name}"
}
dynamic "frontend_ip_configuration" {
   for_each = azurerm_public_ip.pip
   content{
     name                 = "config_${each.value.name}" 
     public_ip_address_id = each.value.id
}

卫斯理, 非常感谢您的回复。它给了我确认我需要看透这一点,并且我实际上正朝着正确的方向前进。我昨晚才能够抽出时间来测试解决方案。不过,它需要更多的研究才能最终让它发挥作用。

尝试使用 each.value 引用一直失败。它抛出了一个错误,比如 each.value 需要与 for_each 一起使用...我发现这很令人沮丧,因为 for_each 只是我试图 use/reference 的地方上方的 2 行它。此外,在错误中它创建了 3 条错误消息,因此它使用 for_each 进行迭代。无论出于何种原因,它都无法使用 each.value.

提取值

最终适用于我的 LB/Azure 可用区的代码如下(提示,必须使用迭代器选项):

resource "azurerm_lb" "lb" {
  name                = "externallb"
  location            = "${azurerm_resource_group.rg.location}"
  resource_group_name = "${azurerm_resource_group.rg.name}"
  sku                 = "standard" #standard SKU needed to support zones
  dynamic "frontend_ip_configuration" {
    iterator = pub
    for_each = azurerm_public_ip.pip 
    content {
      name                 = "config_${pub.value.ip_address}"
      public_ip_address_id = pub.value.id
    }

  }
}

....

resource "azurerm_lb_nat_rule" "lbnr" {
  count                          = "${var.nblinuxvms}"
  resource_group_name            = "${azurerm_resource_group.rg.name}"
  loadbalancer_id                = "${azurerm_lb.lb.id}"
  name                           = "SSHHost${count.index}"
  protocol                       = "Tcp"
  frontend_port                  = "${2200 + count.index}"
  backend_port                   = 22
  frontend_ip_configuration_name = "config_${azurerm_public_ip.pip[count.index].ip_address}"
}

根据我对迭代器选项的理解,您不必使用它,可以直接将动态块标签中的内容作为前缀引用,但那样会非常长且笨拙。我上面 posted 的代码示例是功能代码 :-)。当那件事发生时我很高兴。花了几天时间试图解决这一切。

至于你最后关于这不是版本 11 并且我不需要所有插值语法的声明,我正在努力清理它。这是我需要完成的待办事项列表中的任务之一,因为我完成了这个功能齐全的 VM 模块的项目。如果 Terraform 注册表在其计算模块中包含 Azure Zones,本可以为我节省大量时间,但在构建所有这些代码时,我必须比只调用注册表更好地学习语言。

就像我上面说的,再次感谢 post 的回答,它证实了我走在了正确的道路上。希望我投入的所有时间都可以帮助其他人 运行 遇到类似的问题。请注意阅读本文的任何人,我有一个 azurerm_public_ip 块,可以根据 VM 和 LB 的需要创建尽可能多的 PIP。如果有人要我添加该代码,我可以。给我留言或评论我的 post.

干杯, -山姆·卡查尔