Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Subsequent variable arithmetics / interpolation when count is zero #15993

Closed
Artiax opened this issue Sep 1, 2017 · 6 comments
Closed

Subsequent variable arithmetics / interpolation when count is zero #15993

Artiax opened this issue Sep 1, 2017 · 6 comments

Comments

@Artiax
Copy link

Artiax commented Sep 1, 2017

Terraform Version

0.9.11

Terraform Configuration Files

resource "aws_route" "peer_route" {
  count                       = "${length(data.aws_vpc_peering_connection.peer_connection.*.id) > 0 ? (length(var.peer_environment_groups) * length(var.route_table_ids)) : 0}"

  route_table_id              = "${element(var.route_table_ids, ceil(count.index / length(var.peer_environment_groups)))}"

The above would fail with the following error (as var.peer_environment_groups in route_table_id is zero):

module.routes_pr.aws_route.peer_route: __builtin_IntMath: divide by zero in:

${element(var.route_table_ids, ceil(count.index / length(var.peer_environment_groups)))}

I've got a workaround for it, however it would be nice if I didn't have to do it:

route_table_id              = "${element(var.route_table_ids, ceil(count.index / (length(var.peer_environment_groups) > 0 ? length(var.peer_environment_groups) : 1)))}"

Explanation: Dividing by 1 rather than 0 when peer_environment_groups length is zero, this won't actually do anything but unblocks the plan and doesn't produce the error anymore.

Expected Behavior

Ignore the subsequent variables/function calls when count is zero.

Actual Behavior

Terraform proceeded to interpolate the variables and carry out the arithmetics when the count of the resource is zero.

@jcrsilva
Copy link

Also affected by this. If count==0 the other variables shouldn't be interpolated.

@farvour
Copy link

farvour commented May 24, 2018

I am also impacted by this scenario. FWIW It doesn't seem to affect every resource type, as I have two different resource types defined doing a modulo on 0 when count for both resources is zero. However, the ebs volume attachment resource has problems and spits a divide by zero error.

resource "aws_ebs_volume" "default" {
  count             = "${var.ebs_volumes_count * var.number}"
  availability_zone = "${element(aws_instance.default.*.availability_zone, count.index)}"
  size              = "${lookup(var.ebs_volumes, "size")}"
  type              = "${lookup(var.ebs_volumes, "type", var.ebs_volume_default_type)}"

  tags = "${merge(var.default_tags, map(
    "Name", "${var.role}-${format("%s",var.function != "" ? "${var.function}-" : "")}${var.environment}-${format("%03d", count.index + 1)}${element(aws_instance.default.*.availability_zone, count.index)}-ebs-${count.index % var.ebs_volumes_count}",
    "VolumeIndex", "${count.index % var.ebs_volumes_count}",
    "Role", "${var.role}",
    "Function", "${var.function}",
    "Environment", "${var.environment}",
    "Region", "${data.aws_region.current.name}",
  ))}"
}

resource "aws_volume_attachment" "default" {
  count       = "${aws_ebs_volume.default.count}"
  device_name = "${element(var.volume_device_names, count.index % (var.ebs_volumes_count > 0 ? var.ebs_volumes_count : 1))}"
  instance_id = "${element(aws_instance.default.*.id, count.index)}"
  volume_id   = "${element(aws_ebs_volume.default.*.id, count.index)}"
}

I applied the workaround in this issue and it seems to resolve the plan problem.

@yruss972
Copy link

yruss972 commented Aug 5, 2018

+1

@apparentlymart
Copy link
Contributor

Posting +1 comments doesn't do anything except create noise for the people following this issue. To influence prioritization, please add a 👍 reaction to the original comment.


With this said: Terraform's behavior here is going to change slightly in the next major release to improve the situation here, but not in exactly the way described. It is intentional that Terraform validates the configuration within a resource block even when count = 0 because the purpose of validation is to check for static (that is, regardless of particular input variable values) problems, similar to how a type checker in a statically-typed programming language will report a problem even if it's in a codepath you aren't calling. The goal is to give you early notice of problems that might otherwise be hidden from you until some later point when you set count to a value greater than zero, where the person encountering the problem might be someone else entirely.

I understand that there are some situations where this behavior is inconvenient, because as the author you know that in practice a certain problem only occurs with count = 0. To make this more convenient in many cases while still retaining the goals of the validation pass, the new compromise is as follows:

During validation, Terraform will check the expressions under the assumption that count.index is a number but it will not assume any particular value for that number. This situation is similar to what happens if you interpolate an attribute of a resource that won't be known until after apply (currently displayed as <computed> in the UI in 0.11): Terraform will be able to ensure that you are using it in a way that makes sense for a number to be used but won't raise any problems that depend on the particular value of that number.

Taking the example from the original comment here:

route_table_id = "${element(var.route_table_ids, ceil(count.index / length(var.peer_environment_groups)))}"

Terraform will evaluate the argument to that ceil call as <unknown number> / 0, which is type-safe. Fortunately, the unknown value short-circuit rules in the checker will cause Terraform to skip trying to evaluate this expression, because an unknown value divided by anything is another unknown value.

This should therefore pass validation and then not be considered again by later passes due to the zero count.

However (for completeness) if we were to instead write something like this:

route_table_id = count.index - "not a number"

...the validation pass would still flag this as an error, even if count = 0, since the language knows that "not a number" can't possibly be a number and thus can't possibly be subtracted from a number, regardless of what value count.index eventually has.

It's likely that there will still be some cases where the static checker will flag things overly-conservatively. That's a consequence the safety/flexibility tradeoff we're trying to make here, but since the next major release is also planned to fix #15605 it should be possible to give the validator additional hints by placing the troublesome expression in a conditional clause that isn't evaluated when count = 0.

@apparentlymart
Copy link
Contributor

Hi all!

The changes I mentioned in my previous comment were included in Terraform v0.12.0, which has now been released.

@ghost
Copy link

ghost commented Jul 25, 2019

I'm going to lock this issue because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues.

If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.

@ghost ghost locked and limited conversation to collaborators Jul 25, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants