Terraform - 条件数据源

Terraform - Conditional Data Source

在 terraform 中,有什么方法可以有条件地使用数据源吗?例如:

 data "aws_ami" "application" {
     most_recent = true
     filter {
         name = "tag:environment"
         values = ["${var.environment}"]
     }
     owners = ["self"]
}

我希望能通过命令行传入一个环境变量,并据此判断是否获取这个数据源。

我知道您可以使用资源 count 属性,但您似乎无法将其用于数据源。

我会考虑将此代码隐藏在模块中,但模块也不能使用 count 参数。

最后,另一个选项是为数据源提供一个“默认”值,如果它返回 null,但我认为这也不可行。

是否还有其他可能的解决方案?

您可以像对资源一样对数据源使用条件,也可以对模块使用 Terraform 0.13+:

variable "lookup_ami" {
  default = true
}

 data "aws_ami" "application" {
   count       = var.lookup_ami ? 1 : 0
   most_recent = true
   filter {
     name   = "tag:environment"
     values = [var.environment]
   }
   owners = ["self"]
}

Terraform 0.12+ 中的一个用例是利用三元语句的惰性求值,如下所示:

variable "internal" {
  default = true
}

data "aws_route53_zone" "private_zone" {
  count        = var.internal ? 1 : 0
  name         = var.domain
  vpc_id       = var.vpc_id
  private_zone = var.internal
}

data "aws_route53_zone" "public_zone" {
  count        = var.internal ? 0 : 1
  name         = var.domain
  private_zone = var.internal
}

resource "aws_route53_record" "www" {
   zone_id = var.internal ? data.aws_route53_zone.private_zone.zone_id : data.aws_route53_zone.public_zone.zone_id
   name    = "www.${var.domain}"
   type    = "A"
   alias {
     name                   = aws_elb.lb.dns_name
     zone_id                = aws_elb.lb.zone_id
     evaluate_target_health = false
   }
}

var.internaltrue 时,这将在私有区域中创建一条记录,而当 var.internalfalse 时,将在 public 区域中创建一条记录].

对于这个特定的用例,您还可以使用 Terraform 0.12+ 的 null 更简单地重写它:

variable "internal" {
  default = true
}

data "aws_route53_zone" "zone" {
  name         = var.domain
  vpc_id       = var.internal ? var.vpc_id : null
  private_zone = var.internal
}

resource "aws_route53_record" "www" {
   zone_id = data.aws_route53_zone.zone.zone_id
   name    = "www.${data.aws_route53_zone.zone.name}"
   type    = "A"
   alias {
     name                   = aws_elb.lb.dns_name
     zone_id                = aws_elb.lb.zone_id
     evaluate_target_health = false
   }
}

如果 var.internal 设置为 true,这只会将 vpc_id 参数传递给 aws_route53_zone 数据源,因为您无法设置 vpc_idprivate_zonefalse.


旧 Terraform 0.11 和更早的答案:

您实际上可以根据数据源的数量使用条件,但我已经尝试过,但我还没有设法为它制定一个好的用例。

举个例子,我成功地完成了这个工作:

data "aws_route53_zone" "private_zone" {
  count        = "${var.internal == "true" ? 1 : 0}"
  name         = "${var.domain}"
  vpc_id       = "${var.vpc_id}"
  private_zone = "true"
}

data "aws_route53_zone" "public_zone" {
  count        = "${var.internal == "true" ? 0 : 1}"
  name         = "${var.domain}"
  private_zone = "false"
}

但随后在如何 select 它的输出方面遇到了问题,因为 Terraform 将在决定使用三元条件的哪一侧之前评估三元条件中的任何变量(而不是惰性评估)。所以这样的事情不起作用:

resource "aws_route53_record" "www" {
   zone_id = "${var.internal ? data.aws_route53_zone.private_zone.zone_id : data.aws_route53_zone.public_zone.zone_id}"
   name = "www.example.com"
   type = "A"
   alias {
     name                   = "${aws_elb.lb.dns_name}"
     zone_id                = "${aws_elb.lb.zone_id }"
     evaluate_target_health = "false"
   }
}

因为如果 internal 为真,那么您将获得 private_zone 数据源而不是 public_zone 数据源,因此三元组的后半部分无法计算,因为 data.aws_route53_zone.public_zone.zone_id 未定义,反之亦然。

在您的情况下,您可能只想有条件地使用数据源,因此可以执行如下操作:

variable "dynamic_ami" { default = "true" }
variable "default_ami" { default = "ami-123456" }

data "aws_ami" "application" {
  most_recent = true
  filter {
    name = "tag:environment"
    values = ["${var.environment}"]
  }
  owners = ["self"]
}

resource "aws_instance" "app" {
    ami = "${var.dynamic_ami == "true" ? data.aws_ami.application.id : var.default_ami}"
    instance_type = "t2.micro"
}