terraform icon indicating copy to clipboard operation
terraform copied to clipboard

XOR operation to describe validation rules for mutually-exclusive attributes

Open rpadovani opened this issue 2 years ago • 4 comments

Current Terraform Version

Terraform v1.1.6
on linux_amd64

Use-cases

I am using the optional keyword to define an object with two properties, which are both optional, but in which at least one must be populated.

For doing so, I am using validation to enforce this condition. While it is possible, a XOR operator would make it way easier. Especially 'cause the workaround explodes in complexity when we are talking about multiple keys to check, and not just two.

Attempted Solutions

variable "example" {
  type = object({
    first = optional(string)
    second = optional(string)
  })

  validation {
    condition = (
      # One of the following two has be true: one is set, and the other not.
      (lookup(var.example, "first", null) != null && lookup(var.example, "second", null) == null) ||
      (lookup(var.example, "first", null) == null && lookup(var.example, "second", null) != null)
    )
    
    error_message = "First or second must be set, but not both"
  }
}

Proposal

A XOR operator, in this example, ^,

variable "example" {
  type = object({
    first = optional(string)
    second = optional(string)
  })

  validation {
    condition = lookup(var.example, "first", null) != null ^ lookup(var.example, "second", null) != null
    
    error_message = "First or second must be set, but not both"
  }
}

rpadovani avatar Feb 22 '22 14:02 rpadovani

Thanks for the feature request!

crw avatar Feb 23 '22 19:02 crw

I remember back when we were considering which operators to make builtins we had intentionally made the "power of" operation be a function rather than an operator both because it seems to be infrequently needed in Terraform (it's not an environment intended for mathematical work) and because the meaning of ^ is ambiguous where some other languages use it to represent power of while others use it to represent XOR.

I think the same argument holds here too: XOR seems like an operation that is occasionally useful but not frequently used, and of course the ambiguity with ^ still applies from this direction too.

Given that, I would suggest that if we do support it then we should do so using a function named xor that takes two arguments. I think we do still need to weigh whether it would be used often enough to justify it being first-class at all, but the bonus of the function formulation is that the bar it needs to meet in that regard is likely lower, because we can add new functions to the function table relatively easily without affecting any other software, whereas a new operator would need to be co-designed with various other products using the underlying HCL engine.

apparentlymart avatar Feb 24 '22 16:02 apparentlymart

Incidentally, I think the lookup calls in this example aren't really doing anything because the type constraint already requires both of those arguments to be present in the object; the optional just makes Terraform fill them in as null when not specified by the caller. That means you could write var.example["first"] != null instead with equivalent effect, which hopefully allows for at least a little simplification of that complex expression with today's Terraform.

apparentlymart avatar Feb 24 '22 16:02 apparentlymart

Given that, I would suggest that if we do support it then we should do so using a function named xor that takes two arguments.

I like this idea as well! Makes sense if it is easier to implement, and it sounds something doable.

var.example["first"] != null

Thanks for the suggestion! It definitely makes the code more readable!

rpadovani avatar Feb 25 '22 21:02 rpadovani