【问题标题】:Terraform - Conditional Data SourceTerraform - 条件数据源
【发布时间】:2017-06-11 01:10:21
【问题描述】:

在 terraform 中,有没有办法有条件地使用数据源?例如:

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

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

我知道您可以使用 count 属性来处理资源,但您似乎不能将其用于数据源。

我会考虑将这段代码放在一个模块中,但模块也不能使用count 参数。

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

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

【问题讨论】:

    标签: amazon-web-services terraform


    【解决方案1】:

    您可以像使用资源一样对数据源使用条件,也可以在模块上使用 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 时在公共区域中创建一条记录。

    对于这个特定的用例,您还可以使用 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 数据源,因为当private_zonefalse 时,您无法设置vpc_id


    旧 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"
    }
    

    但是在如何选择它的输出方面遇到了问题,因为 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"
    }
    

    【讨论】:

    • 感谢您提供的所有信息。我不知道我可以在数据源中使用计数。但是, id 可能希望更像您在第一个示例中那样使用它。问题是,如果我的动态数据源还不存在,那么它对 aws 的查询会导致失败。因此,仅将三元组放入资源中是不够的。不过,我将不得不对此进行试验。谢谢!
    • 一个很好的用例是,如果您有一个由多个工作区共享的资源,并且只希望在部署某个工作区时创建它。因此,您的“prod”工作区可能正在配置资源 X,而您的“测试”工作区可能会将 X 引用为数据源。
    • 为什么数据源需要有条件?当然只是使用数据源输出吗?而且,如前所述和 0.12 之前的版本,如果您将数据源设置为有条件的,那么任何依赖于它(甚至是有条件的)的东西都将由于 HCL1 中三元语句的急切评估而失败。
    猜你喜欢
    • 2021-09-25
    • 2018-01-21
    • 2021-01-12
    • 2017-12-09
    • 2019-05-19
    • 2021-08-07
    • 1970-01-01
    • 1970-01-01
    • 2019-12-20
    相关资源
    最近更新 更多