Infrastructure

Terraform: How to fix “Inconsistent conditional result types”

Last week, on my job, I encountered this error and couldn’t find a solution available online, so I decided to write the solution I came up with.

  1. Problem
  2. Context
  3. Solution
    1. True case
    2. False case
  4. Conclusion

Problem

When we have a ternary, both results must have the same structure.

locals {
  ingress_config = var.is_open ? {
    enabled: true
    config: {
      path: "/dashboard"
    }
  } : {
    enabled: false
  }
}

That leads to:

This only happens when there is an inner map in the map, which makes sense because if we instead had:

locals {
  ingress_config = var.is_open ? {
    enabled: true
    path: "/dashboard" # Specified directly
  } : {
    enabled: false
  }
}

Terraform could infer that path is optional and in the “else” block is null.

Context

This arose in my job, with that exact example, we were creating a module for provisioning a Helm release (for releasing our Chart), which had the ingress as optional, since the Chart had a structure similar to the example above, it led to that error.

After a thorough search, I couldn’t find any fix that didn’t involve changing the structure. (which isn’t really a fix)

Thus, I thought of a creative solution.

Solution

The idea for this solution came to mind while dealing with encodings – the Helm provider deals with YAML directly, so I always have to encode this map to YAML.

The fix for this is using yamlencode in those objects and then yamldecode the ternary result. This way, both ternary results are strings.

locals {
  ingress_config = yamldecode(var.is_open ? yamlencode({
    enabled: true
    config: {
      path: "/dashboard"
    }
  }) : yamlencode({
    enabled: false
  }))
}

Now the plan passes and we can see the result is exactly what we want.

True case

False case

Conclusion

We’ve just seen how to get around this Terraform error by leveraging the YAML encoding.

While Terraform is great for provisioning Infrastructure as code, when it comes to dealing with types, it lacks some flexibility. Fortunately, there is always a way around it!

Side note: If you’re wondering why I needed to decode the YAML again, despite mentioning that the Helm provider handles YAML directly, it’s because we had numerous maps that needed to be merged into a single map.

Leave a comment