Terraform: Невозможно создать vm позади lb с помощью azurerm_lb_backend_address_pool - PullRequest
0 голосов
/ 19 сентября 2019

У меня странное поведение, когда я пытаюсь создать Azure lb с vm внутри внутреннего пула одновременно.

У меня есть модуль для управления vm и один модуль для управления 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 Ответ

2 голосов
/ 19 сентября 2019

Как указано в сообщении об ошибке, значение count не должно зависеть от любых значений, которые Terraform не будет знать до тех пор, пока не будет завершено применение.В этом случае, похоже, var.azure_lb_public_backend_id - это идентификатор объекта, который не будет выделен до тех пор, пока этот объект не будет создан, и поэтому Terraform еще не знает, какое значение это будет иметь, и поэтому не может сказать навернякабудет ли оно равным "".

Чтобы сделать эту работу, вам вместо этого нужно будет принять решение на основе того, что Terraform знает во время планирования.Один из способов сделать это - обернуть ваше значение идентификатора балансировщика нагрузки в объект, чтобы решение могло быть принято на основе того, установлен ли объект вообще:

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 имеет значение null, а не значение атрибута 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 во всех случаях.


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

ВВаш корневой модуль:

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 -ности этого объекта, а вызывающий модуль - это просто простое соединение модулей без какой-либо специальной логики.

Опять же, важная деталь заключается в том, что решение в конечном итоге принимается только на основе переменной var.load_balancer_enabled (косвенно), а не на любых значениях, которые Terraform узнает во время применения.

...