Terraform 13,根据另一个变量的值验证变量

16

有没有一种实现以下逻辑的方法

variable "environment" {
  description = "The environment this will be run in can only be set to [preprod|test|prod]"
  type        = string
  default     = "test"
  validation {
    condition     = can(regex("^(prod|preprod|test)$", var.environment))
    error_message = "The environment variable can only be set to [prod|preprod|test]."
  }
}

variable "fet_code" {
  description = "Set the feature code"
  type        = string
  default     = ""
  validation {
    condition     = var.environment == "test" && length(var.fet_code) != 3
    error_message = "The environment has been set to 'test' but the fet_code has not be defined."
  }
}

目前我遇到以下错误:

Error: Invalid reference in variable validation

  on variable.tf line 17, in variable "fet_code":
  17:     condition     = var.environment == "fet" && length(var.fet_code) == 3

The condition for variable "fet_code" can only refer to the variable itself,
using var.fet_code.

我理解代码存在的问题,只是想知道是否有绕过限制的方法?


你尝试从 fet_codecondition 中实现什么目标还不清楚,能否澄清一下?另外,为什么要检查 environment 变量是否等于 feature,如果根据 environment 上的 condition,这不是允许的输入值之一呢? - Matt Schuchard
@MattSchuchard 感谢指出错误。我认为我已经修复了所有问题。 我的逻辑是,如果变量 environment 被设置为 test,那么变量 fet_code 必须被设置,否则它应该为空。 - alexis
好的,这是不可能的,因为变量的值不能在变量声明内使用,但如果可以的话,您可以使用三元运算符来控制true/false返回,例如:var.environment == "test" ? (length(var.fet_code) == 3) : true。请注意,您还切换了对fet_code长度验证的true/false返回。https://www.terraform.io/docs/configuration/expressions.html#conditional-expressions - Matt Schuchard
6个回答

14

虽然有一个Github问题需要作为特性实现,但是目前唯一验证多个变量的方法是使用locals在运行时抛出错误:

variable "environment" {
  description = "The environment this will be run in can only be set to [preprod|test|prod]"
  type        = string
  default     = "test"
  validation {
    condition     = can(regex("^(prod|preprod|test)$", var.environment))
    error_message = "The environment variable can only be set to [prod|preprod|test]."
  }
}

variable "fet_code" {
  description = "Set the feature code"
  type        = string
  default     = ""
}

locals {
  validate_fet_code_cnd = var.environment == "test" && length(var.fet_code) != 3
  validate_fet_code_msg = "The environment has been set to 'test' but the fet_code has not been defined."
  validate_fet_code_chk = regex(
      "^${local.validate_fet_code_msg}$",
      ( !local.validate_fet_code_cnd
        ? local.validate_fet_code_msg
        : "" ) )
}

这是一种混乱而草率的方法,但它应该可以防止无效的值被应用。


7
我认为在上述的 GitHub 问题中最近提出了一个更清晰的解决方案。请看 https://github.com/hashicorp/terraform/issues/25609#issuecomment-1057614400 - Tata
“混乱的、不可靠的黑客”这个词汇哪里不清楚呢? :P 我的回答只是对于如何验证多个变量的问题的确切回应。我并不建议实际这样做。目前来说,更好的选择是使用 @martin-verner 建议的对象变量方法。 - tzrlk

9

在我看来,这两个变量非常相关。为了实现你想要的结果,我建议将它们改为一个对象。

variable "environment" {
  type = object({
    environment = string
    fet_code = string
  })
  default = {
    environment = "test"
    fet_code = ""
  }

  validation {
    condition     = can(regex("^(prod|preprod|test)$", var.environment.environment))
    error_message = "The environment variable can only be set to [prod|preprod|test]."
  }

  validation {
    condition     = var.environment.environment == "test" && length(var.environment.fet_code) != 3
    error_message = "The environment has been set to 'test' but the fet_code has not be defined."
  }
}

您可以像这样传递变量:
environment = {
  environment = "test"
  fet_code = "1234"
}

4
我自己在类似问题上找到的最好解决方案是将检查置于一个模块中。只有在环境需要设置变量时,该模块才会被包含。
module input_validation {
    source   = "./modules/input_validation"
    count    = var.environment == "test" ? 1 : 0
    fet_code = var.fet_code
}

然后在 modules/input_validation/input_validation.tf 文件中:

variable "fet_code" {
  default = ""
  type    = string
  validation {
    condition     = length(var.fet_code) != 3
    error_message = "The environment has been set to 'test' but the fet_code has not be defined."
  }
}

1
一种解决方案可能是利用 Terraform v1.2.0 及更高版本中提供的 preconditions。它可以在 resourcedataoutputsourcelifecycle 块中设置。
variable "environment" {
  description = "The environment this will be run in can only be set to [preprod|test|prod]"
  type        = string
  default     = "test"
  validation {
    condition     = can(regex("^(prod|preprod|test)$", var.environment))
    error_message = "The environment variable can only be set to [prod|preprod|test]."
  }
}

variable "fet_code" {
  description = "Set the feature code"
  type        = string
  default     = ""
}

resource "resource" "example" {
  attribute = "value"

  lifecycle {
    precondition {
      condition     = var.environment == "test" && length(var.fet_code) != 3
      error_message = "The environment has been set to 'test' but the fet_code has not be defined."
    }
  }
}



0

感谢 @Torge 的评论,正如您所提到的使用本地源模块,可以验证多个变量或本地变量。

例如,在源代码中定义一些本地变量,可以采用硬编码的值或从变量输入中获得。子句的验证由布尔本地变量consolidation解决。

locals {
  node_type = "cache.t1.micro"
  snapshot_retention_limit = 0
  consolidation = local.node_type == "cache.t1.micro" && local.snapshot_retention_limit > 0 ? false : true
}


module "validation" {
  source = "./module"
  
  node_type     = local.node_type
  consolidation = local.consolidation
}

在模块文件夹内,您可以评估单个变量,例如变量node_type,或更复杂的验证,例如变量consolidation

variable "consolidation" {
    type = bool
    description = "consolidation of locals `node_type` & `snapshot_retention_limit` "
    validation {
        condition     = var.consolidation == true
        error_message = "Module parameter `consolidation` can not be equals `false`."
    }
}


variable "node_type" {
  description = "Required AWS values"
  validation {
    condition     = can(regex("^cache\\..*\\..*", var.node_type))
    error_message = "Only nodes starting with cache. are allowed."
  }
}

-1

由于您无法引用特定变量之外的其他变量,因此可以通过将其作为列表使用来以不同的方式完成:

variable "fet_code" {
  description = "Set the feature code"
  type        = list
  default     = ["test", ""]
  validation {
    condition     = var.fet_code[0] == "test" && length(var.fet_code[1]) != 3
    error_message = "The environment has been set to 'test' but the fet_code has not be defined."
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接