当我尝试同时在后端池中用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
为了避免发布过多的代码,我自愿不发布所有代码,但会毫不犹豫地询问更多信息。
您知道为什么我会出现这种情况吗?
答案 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在应用过程中将学习的任何值做出决定。