【问题标题】:If-else condition in terraform data sourceterraform 数据源中的 if-else 条件
【发布时间】:2021-09-25 16:02:23
【问题描述】:

我的问题很简单,但无法在整个互联网上找到任何示例或解决方案。我想在 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 可以有相似的标签,我可以简单地通过变量传递值。但在上述情况下,标签也是不同的。

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

【问题讨论】:

    标签: if-statement conditional-statements terraform datasource terraform-provider-aws


    【解决方案1】:

    你可以用count语句和三元运算符组合来做到这一点。

    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
    }
    

    【讨论】:

    • 我很欣赏你的建议,我希望这能奏效。我已经尝试过了,但是 terraform 不允许有 2 个具有相同名称的数据源:A aws_vpc data resource named "vpc" was already declared。如果我创建具有差异名称的数据源,那么我也必须创建具有不同名称的资源并在那里传递数据值。简而言之,如果我走这条路,我必须创建完全不同的新模块,因为这个小改动。无论如何,谢谢你的回答。我想我可以在 aws 端重命名标签以使其工作。
    • 令人沮丧的是,这样的条件是每种语言的基础。他们可以简单地允许使用条件,从字面上看,这将有助于在更大程度上减少代码大小。我们根本不需要复制代码。
    • 因为如果你有不同的资源用于不同的环境,例如 test/stage/prod,你应该为每个环境创建新的文件夹结构。即使可以使用变通方法,也不要尝试使用条件。那不是可维护的。
    【解决方案2】:
    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"
      }
    }
    

    【讨论】:

    • 为此,两个标签应该是相似的,正如你提到的NameType
    • 哦,我错过了。我的道歉
    【解决方案3】:

    根据您的详细目标,有多种不同的方法可以实现此结果。最直接的答案是使用conditional expression 来选择两个地图值之一:

      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 属性。


    我不知道哪些设计假设和约束对您的系统最重要,因此我不能特别推荐这些方法中的任何一种,但我提出它们是因为根据布尔变量选择两个非常不同的值可以有时会提示separation of concerns不足。

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

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

    【讨论】:

    • 虽然我已经通过注入类似的标签解决了这个问题,所以我使用的条件更少,代码更干净。但是您提到的内容完全有道理,应该用于解决此类问题。因此,对于有同样问题的人来说,接受这个答案。
    猜你喜欢
    • 2019-04-25
    • 2023-01-30
    • 2022-10-16
    • 2017-06-11
    • 1970-01-01
    • 1970-01-01
    • 2021-04-30
    • 2021-06-02
    • 1970-01-01
    相关资源
    最近更新 更多