Terraform valdiate that one of N variables is set to "true"

38 views Asked by At

I have 4 storage class variables that are set by default to false , then set one of them in inputs to "true" . Looking for a way to validate that only one of 4 storage classes variable is set to "true".

variable "sc1_default" { 
  default = "false"
}

variable "sc1_default" { 
  default = "false"
}

variable "sc3_default" { 
  default = "false"
}

variable "sc4_default" { 
  default = "false"
}


   
on terraform.tfvars  Inputs

sc3_default = "true"
3

There are 3 answers

0
Helder Sepulveda On

Option #1

We can do that with a lifecycle precondition in a null_resource

A couple of things before we dive into the code:

  • Your code has two sc1_default variables I'm assuming that the second one was a typo and what you meant there is sc2_default
  • For additional validation use type in the variables, it's a good practice, makes the code more readable and if someone accidentally passes the wrong type the code fails gracefully.

see sample code below

variable "sc1_default" {
  type    = bool
  default = false
}

variable "sc2_default" {
  type    = bool
  default = false
}

variable "sc3_default" {
  type    = bool
  default = true
}

variable "sc4_default" {
  type    = bool
  default = true
}

resource "null_resource" "validation" {
  lifecycle {
    precondition {
      condition = (
        (var.sc1_default ? 1 : 0) +
        (var.sc2_default ? 1 : 0) +
        (var.sc3_default ? 1 : 0) +
        (var.sc4_default ? 1 : 0)
      ) < 2
      error_message = "Only one sc can be true"
    }
  }
}

You can see I set the sc3_default and sc4_default both to true just to trigger the error ...

The condition is the core of this validation we are just adding all the true with the help of shorthand if syntax (var.sc_default ? 1 : 0) and the total should be less than two, I'm assuming that all false is OK, but if not you can change that logic to check that is precisely one.

A terraform plan on that code will error out with the following message:

Planning failed. Terraform encountered an error while generating this plan.

╷
│ Error: Resource precondition failed
│ 
│   on main.tf line 24, in resource "null_resource" "validation":
│   24:       condition = (
│   25:         (var.sc1_default ? 1 : 0) +
│   26:         (var.sc2_default ? 1 : 0) +
│   27:         (var.sc3_default ? 1 : 0) +
│   28:         (var.sc4_default ? 1 : 0)
│   29:       ) < 2
│     ├────────────────
│     │ var.sc1_default is false
│     │ var.sc2_default is false
│     │ var.sc3_default is true
│     │ var.sc4_default is true
│ 
│ Only one sc can be true

Option #2

If you can change the input variables we could reduce it to just one with a list(bool) the code will be smaller and the validation would be right on the variable

variable "sc_default" {
  type        = list(bool)
  description = "list of sc default values"
  default     = [false, false, false, false]


  validation {
    condition     = length(var.sc_default) == 4
    error_message = "Four defaults expected"
  }

  validation {
    condition     = sum([for x in var.sc_default : x ? 1 : 0]) < 2
    error_message = "Only one sc can be true"
  }
}
5
Rui Jarimba On

@helder-sepulveda's answer has a minor bug - validation will fail when all variables are set to "false"

Here's a working example:

variable "sc1_default" {
  default = "true"
}

variable "sc2_default" {
  default = "false"
}

variable "sc3_default" {
  default = "false"
}

variable "sc4_default" {
  default = "true"
}

provider "null" {}

locals {
  joined_scs    = join("", [var.sc1_default, var.sc2_default, var.sc3_default, var.sc4_default])
  scs_are_valid = replace(local.joined_scs, "false", "") == "true"
}

resource "null_resource" "validation" {
  lifecycle {
    precondition {
      condition     = local.scs_are_valid
      error_message = "One and only one SC should be set to true."
    }
  }
}

Running terraform plan:

Planning failed. Terraform encountered an error while generating this plan.

│ Error: Resource precondition failed
│ 
│   on main.tf line 27, in resource "null_resource" "validation":
│   27:       condition     = local.scs_are_valid
│     ├────────────────
│     │ local.scs_are_valid is false
│ 
│ One and only one SC should be set to true.
0
Rui Jarimba On

As an alternative to my other answer, I was just wondering how exactly are you using the storage class variables?

Instead of 4 boolean variables wouldn't it be easier to just declare one string variable and add a condition in order to accept only valid values?

For example, assuming that valid values are sc1, sc2, sc3 and sc4:

variable "storage_class" {
  type        = string
  description = "Value of storage class"

  validation {
    condition     = contains(["sc1", "sc2", "sc3", "sc4"], var.storage_class)
    error_message = "The storage_class must be one of: sc1, sc2, sc3, sc4."
  }
}

Running terraform plan with variable storage_class="abc":

Planning failed. Terraform encountered an error while generating this plan.

╷
│ Error: Invalid value for variable
│ 
│   on main.tf line 1:
│    1: variable "storage_class" {
│     ├────────────────
│     │ var.storage_class is "abc"
│ 
│ The storage_class must be one of: sc1, sc2, sc3, sc4.