Terraform:无法使用azurerm_lb_backend_address_pool

时间:2019-09-19 07:03:37

标签: azure terraform terraform-provider-azure

当我尝试同时在后端池中用vm创建一个Azure lb时,我的行为很奇怪。

我有一个用于管理vm的模块和一个用于管理lb的模块。 如果我先创建lb,那么一切正常,但是如果我两者都创建,那将不起作用。

这是我的配置(terraform 0.12.9,azure 1.33.1):

resource "azurerm_network_interface_backend_address_pool_association" "vm-if-lb-public-association" {
  count                   = var.azure_lb_public_backend_id != "" ? var.countvm : 0
  network_interface_id    = element(azurerm_network_interface.vm-if.*.id, count.index)
  ip_configuration_name   = "${var.workspace_config.prefix}-${var.profile}-${count.index + 1}"
  backend_address_pool_id = var.azure_lb_public_backend_id
}

var.azure_lb_public_backend_id来自我的lb模块

output "lb_id" {
  value = var.enable ? azurerm_lb_backend_address_pool.lb-backend[0].id : ""
}

resource "azurerm_lb_backend_address_pool" "lb-backend" {
  name                = "pool-1"
  count               = var.enable ? 1 : 0
  resource_group_name = azurerm_resource_group.lb-rg[0].name
  loadbalancer_id     = azurerm_lb.lb[0].id
}

运行计划时,我得到以下信息:

Error: Invalid count argument

  on modules/vm/network.tf line 46, in resource "azurerm_network_interface_backend_address_pool_association" "vm-if-lb-public-association":
  46:   count                   = var.azure_lb_public_backend_id != "" ? var.countvm : 0

The "count" value depends on resource attributes that cannot be determined
until apply, so Terraform cannot predict how many instances will be created.
To work around this, use the -target argument to first apply only the
resources that the count depends on.
It's like the plan command doesn't understand vm creation depends on azurerm_lb_backend_address_pool

为了避免发布过多的代码,我自愿不发布所有代码,但会毫不犹豫地询问更多信息。

您知道为什么我会出现这种情况吗?

1 个答案:

答案 0 :(得分:2)

如错误消息所述,count值一定不能依赖于Terraform在应用完成之后才知道的任何值。在这种情况下,var.azure_lb_public_backend_id似乎是一个对象的ID,直到创建该对象后才分配该对象,因此Terraform尚不知道它将具有什么值,因此无法说确定是否等于""

要执行此操作,您需要根据Terraform在计划时间了解的内容做出决定。一种方法是将您的负载均衡器ID值包装在一个对象中,以便可以根据是否设置了该对象来做出决定:

variable "load_balancer" {
  type = object({
    backend_address_pool_id = string
  })
  default = null
}

resource "azurerm_network_interface_backend_address_pool_association" "vm-if-lb-public-association" {
  count                   = var.load_balancer != null ? var.countvm : 0
  network_interface_id    = element(azurerm_network_interface.vm-if.*.id, count.index)
  ip_configuration_name   = "${var.workspace_config.prefix}-${var.profile}-${count.index + 1}"
  backend_address_pool_id = var.load_balancer.backend_address_pool_id
}

现在,决策是基于var.load_balancer对象是否为空,而不是基于其内部backend_address_pool_id属性的值。然后,您的调用模块可以根据用于确定如何在其他模块上设置var.enable的相同测试来设置该值:

  load_balancer = var.load_balancer_enabled ? {
    backend_address_pool_id = module.load_balancer.lb_id
  } : null

假设var.load_balancer_enabled在计划时间是已知的,现在应该可以使用,因为Terraform可以确定load_balancer是否为空,从而在所有情况下确定count的值。 / p>


在上文中,我尝试尽可能地遵循您的安排方式,以便更轻松地了解我所提议的更改,但是可以采用多种不同的方式来安排上述原理,这可能会使模块呼叫者更容易使用。以下是一些与您共享的示例截然不同的示例,它们显示了我们如何在模块界面本身中隐藏此切换的详细信息,以实现更简洁的module composition

在您的根模块中:

variable "load_balancer_enabled" {
  type    = bool
  default = false
}

resource "azurerm_resource_group" "example" {
  name     = "example"
  location = "West US"
}

module "load_balancer" {
  source = "./modules/load-balancer"

  resource_group = azurerm_resource_group.example
  enabled        = var.load_balancer_enabled
}

module "virtual_machines" {
  source = "./modules/virtual_machines"

  resource_group = azurerm_resource_group.example
  vm_count       = 4
  load_balancer  = module.load_balancer
}

load-balancer模块中:

variable "resource_group" {
  type = object({
    name     = string
    location = string
  })
}

variable "enabled" {
  type    = bool
  default = true
}

resource "azurerm_lb" "example" {
  count = var.enabled ? 1 : 0

  name = "example"

  resource_group_name = var.resource_group.name
  location            = var.resource_group.location

  # (and probably a frontend IP allocation)
}

resource "azurerm_lb_backend_address_pool" "example" {
  count = length(azurerm_lb.example)

  name                = "example"
  resource_group_name = var.resource_group.name
  loadbalancer_id     = azurerm_lb.lb[count.index].id
}

output "backend_address_pool" {
  # Set only if the load balancer is enabled. Null otherwise.
  value = var.enabled ? azurerm_lb_backend_address_pool.example[0] : null
}

virtual-machine模块中:

variable "resource_group" {
  type = object({
    name     = string
    location = string
  })
}

variable "vm_count" {
  type = number
}

variable "load_balancer" {
  type = object({
    # We only need to specify the subset of the module outputs
    # that we need here.
    backend_address_pool = object({
      id = string
    })
  })
}

resource "azurerm_network_interface" "example" {
  count = var.vm_count

  # (and whatever other settings you need here)
}

resource "azurerm_network_interface_backend_address_pool_association" "vm-if-lb-public-association" {
  count = var.load_balancer.backend_address_pool != null ? var.vm_count : 0

  network_interface_id    = azurerm_network_interface.example[count.index].id
  backend_address_pool_id = var.load_balancer.backend_address_pool.id
}

在此变体中,load-balancer模块产生一个代表后端地址池的对象,并在禁用该模块时将其设置为null。然后,我们可以将整个模块的结果传递到virtual-machine模块中,并使其基于该对象的null-ness进行决策,而调用模块只是简单地将模块连接在一起而无需任何特殊逻辑。

同样,重要的细节是,最终最终仅根据var.load_balancer_enabled变量(间接)做出决定,而不是根据Terraform在应用过程中将学习的任何值做出决定。