I have a module defined for an EKS cluster using the AWS EKS module, and I am trying to conditionally create a gpu-specific managed node group depending on the cluster.
I have no idea if this is even feasible, I know conditionals within Terraform are classically limiting and frustrating. Here is what I have so far (invalid), but it gets the point across for what I’m trying to do which is conditionally create the gpu managed node group.
which fails with The true and false result expressions must have consistent types. The 'true' value includes object attribute "gpu", which is absent in the 'false' value. when var.enable_gpu is false.
I feel like this should be a relatively common issue, but I can’t find a single instance of someone else trying to do this.
I’m … actually not entirely sure why the Terraform type system is happy with this when it’s not happy with the other variant. It seems to work, though!
I have a feeling that this latter form is appearing to work because Terraform is making an incorrect deduction about what type local.gpu_managed_node_group is intended to be, though I’m not sure cause I’ve not looked closely at what’s going on here.
I think the robust solution here would be to give Terraform more information about what types you are intending these different values to have, so it’s not forced to guess… in the original question it’s guessing incorrectly and therefore returning an error, while in this other variant it seems to be guessing incorrectly in a way that succeeds but might well be doing something unexpected, such as converting all of the numbers to strings so that is can be automatically converted to a map.
From what you’ve shared I understand both gpu_managed_node_group and default_managed_node_group as being maps of a particular object type whose attributes correspond with the arguments of a resource type in AWS, in which case I’d write them out like this so Terraform has more information with which to understand that intent:
With this structure both of the “arms” of the conditional expression are of the same type (map(object({ ... }))) and so Terraform should be able to resolve this expression into a new value of that same type.
The attributes set to null in the first object should be automatically inferred as having the same types as the non-null attributes of the same name in the other object, so the result type will be:
map(object({
ami_type = string
capacity_type = string
instance_types = set(string)
name = string
iam_role_name = string
security_group_name = string
desired_size = number
max_size = number
min_size = number
update_config = object({
max_unavailable_percentage = number
})
schedules = map(object({
recurrence = string
min_size = number
max_size = number
desired_capacity = number
}))
}))
If Terraform does not succeed in deducing this type, then my next step would be to add type conversions to some or all of the null values in the first object, like tomap(null) or tonumber(null); each new type conversion you add removes ambiguity and therefore allows Terraform to more correctly understand what type of value you are intending to construct, without so much guesswork.
Your suggestion is an interesting intellectual exercise in examining the internals of the Terraform type system… but are you really suggesting users write out a great deal of cumbersome something = null boilerplate in real production configs?
It’s not going to make sense to anyone who hasn’t made an in-depth effort to understand the Terraform type system, so isn’t great for Terraform’s reputation as a user-friendly tool.
Playing around with terraform console, I do see the unwanted conversion you described, in a way which would be a problem, if this configuration applied the ?: operator directly to a group definition object value - e.g.:
But it turns out that since this configuration is always working with { name = value } structures, it becomes immune to the issue, as only the top-level type is being coerced to map - compare this example, where the previous value is wrapped in { some_name = ... }:
I am suggesting that when you get type errors in Terraform it typically means that Terraform has reached the wrong conclusion about what you were implying and that you should fix that by implying less. The way I typically imply less is to write out exactly the type I intend to write, and I consider the result to be more readable because it’s now explicit about exactly what type is being written, but I understand that others prefer brevity at the expense of possible ambiguity.
With that said, I would indeed prefer there to be a syntax for doing this via explicit type constraint. Terraform is often held back by its Terraform v0.11-and-earlier legacy where the type system was essentially non-existent, and this is one such case. Hopefully one day Terraform can do better here by making it possible to write out exactly the type you intended as a type constraint, rather than always relying on type inference and then goading Terraform into inferring the correct type via individual type conversion.
I think you’re right though that what you wrote out does allow Terraform to infer your intent correctly. Specifically, in your most recent example Terraform seems to have understood correctly that you intended to return map(object({ami_type = string, desired_size = number})), but the overall merge expression will return different types depending on whether var.enable_gpu is set, which I would consider to be undesirable because it may mask errors elsewhere in the module.