如何使用terraform动态块在az负载均衡器中构建多个前端ip,该负载均衡器利用公共ip来确定azure可用区

时间:2019-11-18 03:42:05

标签: azure foreach load-balancing terraform azure-public-ip

我遇到的问题是关于负载均衡器前端ip的,因此它可以使用唯一的公共IP来访问不同可用区域中的其他服务器。我在创建公共IP时使用了count,但在负载均衡器上却没有使用count,因为我不想为每个服务器都添加一个新的LB。如果我能以某种方式将公共IP保存到变量中,那么我可以在动态块内使用for_each引用它们,但我找不到解决方法。这是我到目前为止的代码,但是不能按原样工作。这个问题可能没有解决方案,这确实很令人讨厌。 顺便说一句,我正在使用下面的split函数,因此它将返回属性所需的列表。有点破烂,但确实有用。

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的动态块似乎是针对特定问题的最佳解决方案,因为它不是一种资源...但是我没有看到一种将公共ip保存到我可以引用的任何变量的方法。如果没有解决方案,人们将如何解决?通过为每个Azure可用性区域创建一个单独的LB?由于它必须是一个标准,而不是看起来成本高昂的基本LB。希望我只是错过了一些东西。任何帮助将不胜感激。注意我只共享了我的terraform项目中的relevent代码。如果需要更多代码,请让我知道。(我的代表声望太低,因此我无法将动态块添加到问题标签中。) 谢谢, -Sam Kachar

2 个答案:

答案 0 :(得分:0)

我觉得您正在尝试做的事情很有意义,应该可以实现。注意事项:

  • for_each块中,您可以使用each.value访问该值(在这种情况下,这是公共ip对象)。有关详情,请参见for_each documentation
  • ip_address是指实际的分配ip,而不是对象。
  • countfor_each块中不可用,因此frontend_ip_configuration块的名称应直接从公共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
}

答案 1 :(得分:0)

卫斯理, 非常感谢您的回复。它给了我确认我需要通读的确认,并且我实际上正朝着正确的方向前进。昨晚我才能够测试解决方案。经过了更多的研究才能最终使它工作。

尝试使用each.value引用一直失败。它抛出了一个类似each.value的错误,需要与for_each一起使用...这让我感到沮丧,因为for_each仅比我尝试使用/引用它的地方高2行。此外,在错误中它创建了3条错误消息,因此它使用for_each进行了迭代。不管出于什么原因,它都无法使用each.value提取值。

最终在我的LB / Azure可用区中工作的代码如下(提示,必须使用iterator选项):

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}"
}

根据我对迭代器选项的了解,您不必使用它,而可以直接将动态块标签中的内容作为前缀引用,但这将是冗长且笨拙的。我上面发布的代码示例是功能代码:-)。当那件事发生时,我感到非常高兴。花了几天的时间来尝试解决所有问题。

至于您关于该版本不是11版本的最后声明,我不需要所有插值语法,我正在努力进行清理。这是我要完成的任务清单上的任务之一,因为我要完成一个功能齐全的VM模块的项目。如果terraform注册表在其计算模块中具有Azure区域,那将为我节省大量时间,但是在构建所有这些代码时,我必须比只调用注册表要好得多地学习语言。

就像我在上面说的那样,再次感谢您发布答案,它证实我正朝正确的道路前进。希望我一直都在帮助别人,如果他们遇到类似的问题。请注意,对于阅读此书的任何人,我都有一个azurerm_public_ip块,该块可创建VM和LB所需的任意数量的PIP。如果有人要我添加该代码,我可以。只是给我发消息或评论我的帖子。

干杯, -Sam Kachar