Terraform: core: length of "splat variable" should not be computed

Created on 19 May 2017  ยท  8Comments  ยท  Source: hashicorp/terraform

I found a case that I would have expected to be fixed by #1497 but that still gives me the same sort of error. It's this below (using the terraform-community-modules tf_aws_vpc and (my fork) of the tf_aws_nat modules):

Thinking a bit more about this I can see why its coming up as computed. The flow is as follows:

  • use data source to get current AZs
  • this is variable is used in a ${slice()} interpolation as the input variable to a module
  • this gets fed into count = "${length(var.public_subnets)}" inside the module
  • The aws_subnet resource with this count uses a splat in the output output "public_subnets" { value = ["${aws_subnet.public.*.id}"] }
  • I then try using the the calling ${length(mod.vpc.public_subnets)}

So I can make the inference between the count and aws_subnet.public.*.id but it's probably one level of indirection too much

Terraform v0.9.5, running terraform plan I get:

data.aws_availability_zones.available: Refreshing state...
data.aws_region.current: Refreshing state...
data.aws_ami.ami: Refreshing state...
Error refreshing state: 1 error(s) occurred:

* module.nat.data.aws_subnet.subnets: data.aws_subnet.subnets: value of 'count' cannot be computed

The terraform code I'm using is:

data "aws_availability_zones" "available" {}
variable "cidr" { default = "10.123.0.0/16" }
module "vpc" {
  source = "github.com/terraform-community-modules/tf_aws_vpc?ref=v1.0.4"
  name = "my-vpc"

  enable_dns_hostnames = true
  enable_dns_support = true

  azs = "${data.aws_availability_zones.available.names}"
  cidr = "${var.cidr}"

  private_subnets = [ "${
    slice(list(
      cidrsubnet(var.cidr, 8, 0),
      cidrsubnet(var.cidr, 8, 1),
      cidrsubnet(var.cidr, 8, 2),
      cidrsubnet(var.cidr, 8, 3),
      cidrsubnet(var.cidr, 8, 4),
    ), 0, length(data.aws_availability_zones.available.names))
  }" ]
  public_subnets = [ "${
    slice(list(
      cidrsubnet(var.cidr, 8, 100),
      cidrsubnet(var.cidr, 8, 101),
      cidrsubnet(var.cidr, 8, 102),
      cidrsubnet(var.cidr, 8, 103),
      cidrsubnet(var.cidr, 8, 104),
    ), 0, length(data.aws_availability_zones.available.names))
  }" ]
}

module "nat" {
  source                 = "github.com/ashb/tf_aws_nat?ref=43f7249"
  name                   = "my-nat"
  instance_count         = 1
  instance_type          = "t2.micro"
  subnet_ids      = ["${module.vpc.public_subnets}"]
  az_list                = ["${module.vpc.public_subnets}"]
  vpc_security_group_ids = []
  aws_key_name           = ""
}
config enhancement

Most helpful comment

Hi all! Over in #10857 is a broader discussion about computed counts, but my latest comment includes my verification that the particular bug reported by _this_ issue has been fixed in Terraform v0.12.0-alpha1.

What fixed this is a removal of the rule that a known list may not contain a computed (now called "unknown") value: the list remains known, and thus its length is known, and only accessing the specific unknown element directly will result in an unknown value.

This fix is already in master and will be included in the v0.12.0 final release, and so I'm going to close this out. Thanks for reporting this bug, and thanks also for your patience while we laid the groundwork to be able to resolve it.

All 8 comments

Hi @ashb! Thanks for following up with this extra detail.

If I'm understanding correctly, I think the way this violated your expectations is that even though the values of the list are computed, Terraform _should_ know the _length_ of the list, because that's derived from a variable.

If so, then I agree: Terraform ought to be able to figure this out in an ideal world. Right now this information doesn't thread through properly because there is a rule that if a list contains any unknown values then it becomes entirely unknown when returned from an interpolation sequence; this is a limitation we've inherited from Terraform's core architecture, and so not something we can fix quickly.

I think is _is_ something we should be able to resolve as part of some holistic work we're hoping to do soon to improve consistency how Terraform passes values around. This should allow us to preserve details like "unknown" properly as they thread between different subsystems. That work is still in the early planning/prototyping phase right now, so I can't say for sure, but we'll keep this issue here for now and update when there's something more concrete and definite to say here.

Thanks again for filing this!

Terraform _should_ know the _length_ of the list, because that's derived from a variable.

Yeah, that was why I was somewhat surprised that it didn't work, but when I wrote it all out in detail I realised why it wasn't working right now.

In my case the work around was easy enough (just create a count variable to the module and call length(data.aws_availability_zones.available.names) myself).

Terraform should know the length of the list, because that's derived from a variable.

@apparentlymart, I would go a step further and say Terraform should know the length of the list if it is a fixed length, even if every value is computed.

I've been looking around at the many variations on this issue and I certainly see that many times the length of the list is computed so it can't be known at plan time. I think there are times where the length of the list is fixed, but the values are computed. So the length isn't even from a variable, there's just two items, but they have computed values so terraform somehow doesn't know how many items are in the list. Example:

resource "aws_iam_policy" "foo" { }
resource "aws_iam_policy" "bar" { }
module "baz" {
  iam_policies = [
    "${aws_iam_policy.foo.arn}",
    "${aws_iam_policy.bar.arn}",
  ]
}

# then in the module
variable "iam_policies" {
  type = "list"
  default = []
}

resource "aws_iam_role_policy_attachment" "policy" {
  policy_arn = "${var.iam_policies[count.index]}"
  role = "${aws_iam_role.foo.name}"
  count = "${length(var.iam_policies)}"
}

The workaround is to just pass in the length in another variable, since I know what it is right there where I'm setting the value of the list. But that seems kinda silly.

module "baz" {
  iam_policies = [
    "${aws_iam_policy.foo.arn}",
    "${aws_iam_policy.bar.arn}",
  ]
  iam_policy_count = 2
}

Or the two-pass plan where I do an apply to make the policies and then the plan that passes them into the module. No matter what I pick it seems like a hack

I am running into the same problem while trying to create aws_ssm_parameters by passing it a map.

I have a map with fixed number of keys, but their values are coming from module outputs or other resources.
I am then passing this map to the SSM_parameters module and try to create the parameters in the following way :

locals {
   ssm_map = {
      DB_NAME = "wordpress"
      DB_USERNAME = "wordpress-user"
      DB_PASSWORD = "${random_string.rds_password.result}"
      DB_HOSTNAME = "${module.rds_instance.hostname}"
   }
}

module "ssm_parameters" {
  source    = ....
  ssm_map = "${local.ssm_map}"
  ...
}


....
# ssm_parameter module

resource "aws_ssm_parameter" "configs" {
  count  = "${length(keys(var.ssm_map))}"
  ...
}

When I am trying to use. count = ${length(keys(var.ssm_map))} I get value of 'count' cannot be computed even though I have a fixed number of keys with specific names. Only the values of the keys are computed.

The workaround is to pass another variable ssm_count = 4 but this doesn't feel right at all.

@mildwonkey I don't really understand why 10857 was closed and merged into this one, considering that 10857 is more than a year older than this issue and has >5x the participants. Relatedly, I never got a reply to my question at https://github.com/hashicorp/terraform/issues/10857#issuecomment-363262914

Oh nevermind, it looks like https://github.com/hashicorp/terraform/issues/12570 has a lot of discussion on it (I think the two issues linked from https://github.com/hashicorp/terraform/issues/10857#issuecomment-422896083 actually have less conversation than that one)

Hi all! Over in #10857 is a broader discussion about computed counts, but my latest comment includes my verification that the particular bug reported by _this_ issue has been fixed in Terraform v0.12.0-alpha1.

What fixed this is a removal of the rule that a known list may not contain a computed (now called "unknown") value: the list remains known, and thus its length is known, and only accessing the specific unknown element directly will result in an unknown value.

This fix is already in master and will be included in the v0.12.0 final release, and so I'm going to close this out. Thanks for reporting this bug, and thanks also for your patience while we laid the groundwork to be able to resolve it.

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.

Was this page helpful?
0 / 5 - 0 ratings