Terraform: null value is not treated per docs when passed to modules

Created on 18 Feb 2020  Â·  5Comments  Â·  Source: hashicorp/terraform

Terraform Version

Terraform v0.12.20

Terraform Configuration Files

main.tf:

module mod1 {
  source         = "./mod1"
  optional_input = null
}

mod1/main.tf:

variable optional_input {
  default = 5
}

output foo {
  value = var.optional_input / 5
}

Debug Output

https://pastebin.com/raw/NuxLJi2U

Expected Behavior

As per docs:

null: a value that represents absence or omission. If you set an argument of a resource or module to null, Terraform behaves as though you had completely omitted it — it will use the argument's default value if it has one

Actual Behavior

An error message is shown saying that the said argument must not be null:

  on mod1/main.tf line 6, in output "foo":
   6:   value = var.optional_input / 5
    |----------------
    | var.optional_input is null

Error during operation: argument must not be null.

Steps to Reproduce

  1. terraform init
  2. terraform apply

References

  • #22430
  • #21702
breaking-change bug config v0.12

Most helpful comment

Hi @oxplot,

I agree that the behavior is inconsistent here, and that's confusing. My sense of the ideal behavior is that if a variable value is set to null then the default value should be taken, and that setting a required variable (one without a default) should be an error.

The design challenge in this area is that some modules use null as a valid value, and so changes to the behavior must consider that. In the provider schema system that is represented by explicitly marking a variable as "optional" without providing a default. That approach doesn't directly work for input variables because the presence of default is overloaded to _imply_ "optional", rather than it being a separate argument.

We could potentially address this by allowing default = null as a special way to declare a variable as optional without a default value. That is still inconsistent with the usual meaning of null as being equivalent to omitting the argument -- omitting default would have a different meaning than setting it to null -- but a special case unique to the variable default argument is better than one that applies to all input variables, and default doesn't accept conditional expressions anyway so there isn't really any other useful meaning of default = null.

Unfortunately, given that the current behavior was established in v0.12.0 we cannot just change it now because there will be many modules depending on the current behavior. I'm going to mark this as a breaking change to acknowledge that while it's a valid issue to be addressed we are blocked by finding a responsible way to introduce it that will not cause confusing misbehavior for existing modules. I don't yet know what that approach will be.

In the meantime, I think it'd be best to update the documentation for module blocks to be explicit about this exception to the usual interpretation of null, so that the behavior is at least well-defined even if also inconsistent.

Thanks for reporting this!

All 5 comments

This forces module consumers to have knowledge of the default value for the module and specify the same default in their variable definitions. This really should be fixed.

Can reproduce in 0.12.25, see comment in #21702

Hi @oxplot,

I agree that the behavior is inconsistent here, and that's confusing. My sense of the ideal behavior is that if a variable value is set to null then the default value should be taken, and that setting a required variable (one without a default) should be an error.

The design challenge in this area is that some modules use null as a valid value, and so changes to the behavior must consider that. In the provider schema system that is represented by explicitly marking a variable as "optional" without providing a default. That approach doesn't directly work for input variables because the presence of default is overloaded to _imply_ "optional", rather than it being a separate argument.

We could potentially address this by allowing default = null as a special way to declare a variable as optional without a default value. That is still inconsistent with the usual meaning of null as being equivalent to omitting the argument -- omitting default would have a different meaning than setting it to null -- but a special case unique to the variable default argument is better than one that applies to all input variables, and default doesn't accept conditional expressions anyway so there isn't really any other useful meaning of default = null.

Unfortunately, given that the current behavior was established in v0.12.0 we cannot just change it now because there will be many modules depending on the current behavior. I'm going to mark this as a breaking change to acknowledge that while it's a valid issue to be addressed we are blocked by finding a responsible way to introduce it that will not cause confusing misbehavior for existing modules. I don't yet know what that approach will be.

In the meantime, I think it'd be best to update the documentation for module blocks to be explicit about this exception to the usual interpretation of null, so that the behavior is at least well-defined even if also inconsistent.

Thanks for reporting this!

@apparentlymart I think it's important to consider the converse...a default that is non null that a consumer of the module may or may not set, yet the configuration has to allow for it. This gets even more complex in chained module configurations.

Agreed that this is unexpected behavior, especially considering the documentation for null in the Expressions documentation.

null: a value that represents absence or omission. If you set an argument of a resource or module to null, Terraform behaves as though you had completely omitted it — it will use the argument's default value if it has one, or raise an error if the argument is mandatory.

Was this page helpful?
0 / 5 - 0 ratings