Home > database >  Terraform The true and false result expressions must have consistent types
Terraform The true and false result expressions must have consistent types

Time:01-04

I have an issue when I'm trying to get my terraform code more dynamic. Please focus on "vm2" configuration.

1. Working Scenario

In my locals.tf I have :

   vms_configurations = {
    "vm1" = {
      size = "Standard_E4ds_v5"
      vm_backup_policy_frequency = "Weekly"
      vm_backup_weekly = {
        count    = 4
        weekdays = ["Sunday"]
      }
    }
    "vm2" = {
      size                             = "Standard_DS12_v2"
      vm_backup_policy_frequency       = "Daily"
      vm_backup_daily_policy_retention = 7
      vm_backup_weekly = {
      }
    }
  }

In my call to backup module I have :

...
  vm_backup_weekly                 = each.value["vm_backup_weekly"]
  vm_backup_daily_policy_retention = each.value["vm_backup_policy_frequency"] == "Daily" ? each.value["vm_backup_daily_policy_retention"] : null
...

And eveything works fine.

2. Failing scenario

But if I try to remove 'vm_backup_weekly' from my "vm2" configuration in locals.tf like this :

   vms_configurations = {
    "vm1" = {
      size = "Standard_E4ds_v5"
      vm_backup_policy_frequency = "Weekly"
      vm_backup_weekly = {
        count    = 4
        weekdays = ["Sunday"]
      }
    }
    "vm2" = {
      size                             = "Standard_DS12_v2"
      vm_backup_policy_frequency       = "Daily"
      vm_backup_daily_policy_retention = 7
    }
  }

And use a conditional expression in my call to backup module like this :

...
  vm_backup_weekly                 = each.value["vm_backup_policy_frequency"] == "Weekly" ? each.value["vm_backup_weekly"] : {}
  vm_backup_daily_policy_retention = each.value["vm_backup_policy_frequency"] == "Daily" ? each.value["vm_backup_daily_policy_retention"] : null
...

It fails with the message :

│ Error: Inconsistent conditional result types
│
│   on r-vm.tf line 33, in module "vms":
│   33:   vm_backup_weekly                 = each.value["vm_backup_policy_frequency"] == "Weekly" ? each.value["vm_backup_weekly"] : {}
│     ├────────────────
│     │ each.value["vm_backup_policy_frequency"] is "Weekly"
│     │ each.value["vm_backup_weekly"] is object with 2 attributes
│
│ The true and false result expressions must have consistent types. The 'true' value includes object attribute "count", which is absent in the 'false'
│ value.

Do you know what I'm missing ?

CodePudding user response:

Terraform is very picky in ternary operators, but there is a neat trick to make it work with any type on either side:

attribute = [true, "false"][condition ? 0 : 1]

this is not improving readability but allows for any value, no matter what Terraform thinks is correct.

The trick here is to create a tuple of mixed types and then reference the index using the ternary.

In your case this would look something like:

vm_backup_weekly = [each.value["vm_backup_weekly"], {}][each.value["vm_backup_policy_frequency"] == "Weekly" ? 0 : 1] 

As said, it is not the most readable version but it gets rid of the pain in almost all cases.

Depending on how you continue to use the value Terraform might carp again.

In Terramate we implemented tm_ternary() function as the HCL ternary did not solve all our use cases and also tries to evaluate both sides always.

CodePudding user response:

Terraform raises an error here because it isn't clear what type of value you are trying to assign to vm_backup_weekly.

Terraform has a structural type system, which means that two objects have the same type if they have the same set of attribute names and all of the attributes of the same name have the same type.

Your original configuration was working because your outermost value is itself an object type and so attribute vm1 and attribute vm2 are two separate attributes and can have their own types. This therefore only really worked because Terraform misunderstood what you were intending to achieve; you were evidently intending this to be a map of objects, but instead you declared an object with objects inside it and therefore accidentally got a working result even though Terraform misunderstood your intention. You would have seen a similar error if you'd declared your local value explicitly as being a map:

  vms_configurations = tomap({
    "vm1" = {
      size = "Standard_E4ds_v5"
      vm_backup_policy_frequency = "Weekly"
      vm_backup_weekly = {
        count    = 4
        weekdays = ["Sunday"]
      }
    }
    "vm2" = {
      size                             = "Standard_DS12_v2"
      vm_backup_policy_frequency       = "Daily"
      vm_backup_daily_policy_retention = 7
      vm_backup_weekly = {
      }
    }
  })

With the above configuration Terraform will complain that it can't infer what element type you are intending to use for your map because the two example values have different types.

The best answer in both cases is to write out values of the intended type in full.

For your original design:

  vms_configurations = tomap({
    "vm1" = {
      size = "Standard_E4ds_v5"
      vm_backup_policy_frequency = "Weekly"
      vm_backup_weekly = {
        count    = 4
        weekdays = tolist(["Sunday"])
      }
    }
    "vm2" = {
      size                             = "Standard_DS12_v2"
      vm_backup_policy_frequency       = "Daily"
      vm_backup_daily_policy_retention = 7
      vm_backup_weekly = {
        count    = tonumber(null)
        weekdays = tolist([])
      }
    }
  })

This produces a value of the following type:

map(
  object({
    size = string
    vm_backup_policy_frequency = string
    vm_backup_weekly = object({
      count    = number
      weekdays = list(number)
    })
  })
)

In your conditional expression you can get a similar effect by writing out a valid value of the intended return type in the "false" result arm:

  vm_backup_weekly = each.value.vm_backup_policy_frequency == "Weekly" ? each.value.vm_backup_weekly : {
    count    = tonumber(null)
    weekdays = tolist([])
  }

Terraform can sometimes guess what you meant in situations like this, but if you don't give it enough information then it will fail until you provide more specific type information.

  • Related