1

I want to conditionally override a module variable that has a default value at plan time. I.e. when the condition is true an override is provided, when it is false no override is provided and the default value is used. Example:

main.tf:

terraform {
  required_version = ">= 0.14.9"
}

variable "random" {
}

module "my_animal_module" {
  source = "./my-animal-module"
  species = var.random > 7 ? "monkey" : "horse"
}

my-animmal-module/main.tf:

variable species {
  default = "horse"
}

resource "local_file" "animal" {
  content = "${var.species}"
  filename = "./animal.txt"
}

As above, I can just provide the default (species = var.random > 7 ? "monkey" : "horse") but that requires the caller knows the module's default value which breaks encapsulation. An alternative is to use some place holder for the default value like "" then test for that condition in the module and use a different value as suggested in this SO answer. This is slightly better but still tedious and indirect. That SO answer is over 3y old and terraform has changed a lot since then. So I'm wondering, is there is a clean way to solve this yet? Essentially what's needed is the dynamic variable analogy to dynamic blocks but AFAIK it does not yet exist.

spinkus
  • 7,694
  • 4
  • 38
  • 62
  • I eddied the answer. – Marcin Sep 28 '21 at 02:15
  • Indeed your linked answer in the question is a design pattern for Terraform < 0.12. – Matthew Schuchard Sep 28 '21 at 11:03
  • @Marcin your soln is an improvement on previously known idiom, but it's still not 100% clear that there isn't a cleaner soln. An assertion that there isn't a better way than locals is what I was after in an answer. – spinkus Sep 29 '21 at 21:22
  • I'm not aware of better way. But maybe @MartinAtkins will know of one. – Marcin Sep 29 '21 at 23:19
  • spinkus: You may want to see comments on the similar question: https://stackoverflow.com/questions/69165146/how-can-i-make-terraform-replace-a-null-value-with-a-default-value. The extremely knowledgeable ydaetskcoR explains in comments the limitations around what you want to do here. Amusingly, the asker in that question apparently arrived at almost the same answer that @Marcin provided below after reading my comment. In summary, if you want something better, I think you have to wait until if/when HCL3 exists and/or custom TF functions. – Matthew Schuchard Sep 30 '21 at 12:52
  • @MattSchuchard Aha. Yes the crux of that Q is pretty much exactly the same as this one. As per the comment you linked to, using "null" *is* supposed to be the solution here but it's bugged. Such a shame there is no fix or better around to [#24142](https://github.com/hashicorp/terraform/issues/24142) yet. Anyway thanks! – spinkus Sep 30 '21 at 17:30
  • As I thought. You won't find anything vastly different from my answer. If such a solution is a deal breaker, you have to leave terraform and search for better iac tool. – Marcin Sep 30 '21 at 21:29

2 Answers2

3

I would reorganize your module as shown below. Basically you would use local.species value instead of using var.species directly. The local.species would be set based on the values from the parent.

variable species {
  default = null
}

locals {
  defaults = {
    species = "horse"
  }
  species = coalesce(var.species, local.defaults["species"])
}

resource "local_file" "animal" {
  content = "${local.species}"
  filename = "/tmp/animal.txt"
}

Then in the parent:

module "my_animal_module" {
  source = "./my-animal-module"
  species = var.random > 7 ? "monkey" : null
}
Marcin
  • 215,873
  • 14
  • 235
  • 294
  • This is really a variation on the previous known solution except your using an intermediate local instead of an intermediate variable. But I guess local does make a little more sense, as it can't be accidentally overridden. Still weird to me that there is no more direct way to do this. – spinkus Sep 28 '21 at 03:25
  • @spinkus My original answer did not work, thus had to modify it to what you see now. This one works. – Marcin Sep 28 '21 at 03:29
  • 1
    1. I believe variable default values are `null` if unspecified. 2. If the value for `species` is completely based on another variable's value, it feels like that variable should be passed and not `species`, and then the entire logic for the `species` value encapsulated in the `locals`. However, this is the literal correct answer. 3. That `local.species` ternary could probably be `species = coalesce(var.species, local.defaults["species"])` to use the null coalescing pattern to simplify. – Matthew Schuchard Sep 28 '21 at 12:43
  • @MattSchuchard Thanks. I knew that there should be something more nicer then the condition. Forgot about `coalesce`. – Marcin Sep 28 '21 at 20:25
  • 1
    @MattSchuchard "1. I believe variable default values are null if unspecified." I don't think this is true. Variables without defaults must be specified. – spinkus Sep 28 '21 at 21:02
  • @MattSchuchard "2. If the value for species is completely based on another variable's value, it feels like that variable should be passed and not species, and then the entire logic for the species value encapsulated in the locals". Disagree. This logic belongs to the caller not the module. Trying to stuff it into the module results in much more convoluted code. Created a demo [here](https://gist.github.com/sgpinkus/aaea4187ad029d514756bbf5e5d3a7b0). – spinkus Sep 28 '21 at 21:15
  • @spinkus 1. Right, there is a difference in this DSL between the `null` type and `undefined`; explains why I was unsure. 2. So that is not 100% what I meant, but I have no idea how to express it fully in a comment. – Matthew Schuchard Sep 30 '21 at 12:48
1

You can use conditional expression. Please refer below page: https://www.terraform.io/docs/language/expressions/conditionals.html

Or you can use validation inside variable block. Refer below page: https://www.terraform.io/docs/language/values/variables.html

Let me know if it helps

Maya Ray
  • 523
  • 1
  • 7
  • 21