Terraform 数据源中的 If-else 条件

If-else condition in terraform data source

我的问题很简单,但在整个互联网上找不到任何示例或解决方案。我想在 terraform 的 data source 中使用 if-else 条件,并根据它应该搜索结果的值。我们的 AWS 账户中有 2 个不同的 VPC:1. Prod VPC,2. RDS VPC

过滤器应基于布尔变量工作 var.rds_vpc

伪代码:

data "aws_vpc" "vpc" {
  if var.rds_vpc == true:
    tags = {
      Name = "rds_vpc"
      Type = "database"
    }
  else:
    tags = {
      Cluster     = "live_vpc"
      Enviornment = "production"
    }

}

如果两个 VPC 可以有相似的标签,我可以简单地通过变量传递值。但是在上面的例子中,标签也是不同的。

如果有人能提供帮助,我将不胜感激。

您可以使用计数语句和三元运算符组合来实现。

data "aws_vpc" "vpc" {
    tags = {
      Name = "rds_vpc"
      Type = "database"
    }
    count = var.rds_vpc == true ? 1 : 0
}
data "aws_vpc" "vpc" {
    tags = {
      Cluster     = "live_vpc"
      Enviornment = "production"
    }
    count = var.rds_vpc == true ? 0 : 1
}

您可以像这样将标签传递给资源。

variable tag_x {
    type = map(string)
}

variable tag_y {
    type = map(string)
}

tag_x = {
    Name = "rds_vpc"
    Type = "database"
}

tag_y = {
      Cluster     = "live_vpc"
      Enviornment = "production"
}

data "aws_vpc" "vpc" {
    tags = var.rds_vpc == true ? var.tag_x : var.tag_y
}
data "aws_vpc" "vpc" {
  tags =
    var.rds_vpc == true ? "Name" : "Cluster" = var.rds_vpc == true ? "rds_vpc"  : "live_vpc"
    var.rds_vpc == true ? "Type" : "Environment" = var.rds_vpc == true ? "database" : "production"
  }
}

根据您的详细目标,有多种不同的方法可以实现这一结果。最 直接 的答案是使用 conditional expression 到 select 两个映射值之一:

  tags = (
    var.rds_vpc ?
    {
      Name = "rds_vpc"
      Type = "database"
    } :
    {
      Cluster     = "live_vpc"
      Enviornment = "production"
    }
  )

如果您正在编写一个模块,其抽象对于区分两种完全不同的标记结构是有意义的,那么这 可能 是一个合理的方法,但是像这样的结构确实表明您的模块可能试图一次解决太多问题,因此我也会考虑一些不同的设计。


如果您只是想编写一个与虚拟网络一起工作的通用模块,它实际上并不需要“知道”网络真正代表什么,那么您可以考虑让它只接受任意标签作为输入变量并完全按照给定的方式传递它们,然后调用模块可以根据其对 VPC 目的的了解选择不同的标签:

variables "vpc_tags" {
  type    = map(string)
}

data "aws_vpc" "vpc" {
  tags = var.vpc_tags
}

因为看起来您正在编写一个使用 VPC 的模块,而不是管理 VPC 的模块,您也可以考虑使用 dependency inversion 方法,其中模块只是声明它需要一个 VPC,但将其留给调用模块来决定如何提供它:

variables "vpc" {
  # Include here the subset of VPC attributes
  # your module actually needs.
  type = object({
    id         = string
    cidr_block = string
  })
}

您可以在模块的其他地方使用 var.vpc.id,而这些情况与您使用 data.aws_vpc.vpc.id 的情况相同。然后 调用 模块可以使用 data "aws_vpc" 块本身 传入它已经在管理的 VPC,具体取决于什么适合情况:

data "aws_vpc" "rds" {
  tags = {
    Name = "rds_vpc"
    Type = "database"
  }
}

module "uses_vpc" {
  source = "./uses-vpc"

  vpc = data.aws_vpc.rds
}
resource "aws_vpc" "production" {
  cidr_block = "10.1.0.0/16"

  tags = {
    Cluster     = "live_vpc"
    Enviornment = "production"
  }
}

module "uses_vpc" {
  source = "./uses-vpc"

  vpc = aws_vpc.production
}

这种对同一模块使用 data "aws_vpc"resource "aws_vpc" 的方法可行,因为数据资源类型和托管资源类型都具有 idcidr_block 变量类型约束所需的属性。


我不知道哪些设计假设和约束对您的系统最重要,所以我不能特别推荐这些方法中的任何一种,但我提出它们是因为 select两种截然不同的方法取决于布尔变量的值有时可能表明 separation of concerns.

不足

例如,在您的情况下,我会担心我的整个系统是否具有基于 var.rds_vpc 很多 条件,因为这意味着很多刚性你的系统设计。如果以后的需求要求您添加 third VPC,您需要更新多少个不同的模块才能实现?

过早的泛化也可能是有害的——没有必要解决你暂时没有的问题——但我上面建议的两种替代模式是 Terraform 中关注点分离的常见方法这不会导致很多额外的复杂性。