Terraform 解耦安全组依赖

Terraform decouple Security Group dependency

这是使用 Terraform v0.12.9

测试的

我通常将安全组和安全组规则作为单独的资源进行管理,如下例所示:

resource "aws_security_group" "this" {
  count       = var.create ? 1 : 0
  name_prefix = "${var.security_group_name}_"
  vpc_id      = var.vpc_id

  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_security_group_rule" "ingress_rules" {
  count                     = var.create ? length(var.inbound_security_group_ids) : 0

  security_group_id         = aws_security_group.this[0].id
  type                      = "ingress"

  from_port                 = var.from_port
  to_port                   = var.to_port
  protocol                  = "tcp"
  source_security_group_id  = var.inbound_security_group_ids[count.index]
}

这个实现看起来像下面这样:

module "test_module" {
  source                     = "../path/to/module/"

  create                     = true
  vpc_id                     = "vpc-xxxxxx"
  security_group_name        = "${var.service_name}-db"
  from_port                  = 1234
  to_port                    = 1234
  inbound_security_group_ids = [
    module.service.security_group_id_one,
    module.service.security_group_id_two
  ]
}

问题

如果未创建 module.service 的输出,我希望它能工作。在那种情况下,我的期望是 length(var.inbound_security_group_ids) 应该评估为 0 导致不创建安全组规则

实际发生的是 length(var.inbound_security_group_ids) 在未创建 module.service 时计算为 2。这大概是因为它是两个空白字符串的数组 ["", ""]

根据 Terraform documentation,我可以使用 compact 函数来处理这个问题,该函数从数组中删除空字符串。

resource "aws_security_group_rule" "ingress_rules" {
  count                     = var.create ? length(compact(var.inbound_security_group_ids)) : 0

  security_group_id         = aws_security_group.this[0].id
  type                      = "ingress"

  from_port                 = var.from_port
  to_port                   = var.to_port
  protocol                  = "tcp"
  source_security_group_id  = var.inbound_security_group_ids[count.index]
}

然而,这个问题是 Terraform 无法确定 plan,因为它不知道 var.inbound_security_group_ids 的计算结果直到 apply-time。这是错误消息(针对上下文):

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.

问题

是否可以像这样解耦安全组,以便即使 source_security_group_id 属性没有值,它仍然会被创建?

在 Terraform 中处理可能为空的列表或集合通常比处理可能未设置的单个值更容易,原因与您观察到的相关:它将值是否已设置分开从值实际是什么,这样即使值本身未知,也可以知道值的存在

在这种情况下,您可以通过更改 return 来自 service 模块的安全组 ID 的方式来解决这个问题,以便每个输出都是一个 list 的安全组 ID,而不是可能是有效安全组 ID 或空字符串的单个字符串:

module "test_module" {
  source                     = "../path/to/module/"

  create                     = true
  vpc_id                     = "vpc-xxxxxx"
  security_group_name        = "${var.service_name}-db"
  from_port                  = 1234
  to_port                    = 1234
  inbound_security_group_ids = concat(
    module.service.security_group_ids_one,
    module.service.security_group_ids_two,
  )
}

如果已知 security_group_ids_onesecurity_group_ids_two 为空列表,则 concat 将完全忽略它。如果它们 启用,那么您可以将它们安排为一个已知列表,其中一个元素的值未知,因此 length(var.inbound_security_group_ids) 在所有情况下仍将具有已知值。