hashicorp / terraform

Terraform enables you to safely and predictably create, change, and improve infrastructure. It is a source-available tool that codifies APIs into declarative configuration files that can be shared amongst team members, treated as code, edited, reviewed, and versioned.

Home Page:https://www.terraform.io/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

terraform modules value of count cannot be computed

zerolaser opened this issue · comments

I was using terraform modules for IAM user creation, add multiple inline policies and multiple policy_arn's to the user after creation. But now I got an issue where I create an IAM_POLICY and get the arn of this policy and i am trying to add it as policy_arn ="{module.policy.policy_arn}" but i was getting the error, value of 'count' cannot be computed.
My current version of terraform is 0.8.7

module/user/users.tf

variable user {}
variable policy_arn {
   type = "list"
   default = ["default"]
}

variable policy_file {
  type = "list"
  default = ["default"]
}

resource "aws_iam_user" "user" {
  name  = "${var.user}"
}

resource "aws_iam_access_key" "key" {
  user  = "${var.user}"
}

resource "aws_iam_user_policy" "user_policy" {
    count = "${element(var.policy_file, 0) =="default" ? 0: length(var.policy_file)}"
    name = "${element(var.policy_file,count.index)}"
    user = "${var.user}"
    policy = "${file("../policies/${element(var.policy_file,count.index)}.json")}"
    depends_on = ["aws_iam_user.user"]
}

resource "aws_iam_user_policy_attachment" "policy_attach" {
    count = "${element(var.policy_arn, 0) =="default" ? 0: length(var.policy_arn)}"
    user  = "${var.user}"
    policy_arn = "${element(var.policy_arn, count.index)}"
    depends_on = ["aws_iam_user.user"]
}

module/policy/policy.tf

variable policy_file {
  type = "string"
  default = "default"
}

variable description {
  type = "string"
  default = "policy description"
}

resource "aws_iam_policy" "policy" {
    path = "/"
    description = "$(var.description}"
    name = "${var.policy_file}"
    policy = "${file("../policies/${var.policy_file}.json")}"
}

main.tf

module "app_user" {
  source = "../module/user"  
  user = "app-user"     
 policy_file = ["ec2_access", "rds_access" ]
  policy_arn = [ "arn:aws:iam::aws:policy/ReadOnlyAccess","arn:aws:iam::aws:policy/AmazonSQSFullAccess", "${module.test_policy.policy_arn}" ]
}

module "test_policy" {
  source = "../module/policy/policy.tf"
  policy_file = "test_policy"
  description = "Read access to the autoscale event queue"
}

output "policy_arn" {
  value = "${module.test_policy.policy_arn}"
}

when i do terraform plan i was getting the error the aws_iam_user_policy.user_policy: value of 'count' cannot be computed.
now i am not sure. how would i get the arn of the policy created in other module to the current policy_arn to the user.

I tried with terraform 0.9.0 dev its showing the same issue. but if i first apply with target module on the policy then apply for user, Its not throwing any count error. Its working. I might need a way to tell terraform to apply policy module first then apply user module. It should be done with depends_on but i'm not able to call depends_on on other modules. Could we write a null_resource depending on policy and user module depending on null_resource ?

Any suggestions/workarounds or modifications to my modules will be appreciated. thanks.

I've seen a similar problem. There seems to be a general issue where the deferred evaluation of the count attribute of any resource declared in a module is performed. In my case, I'm getting a circular dependency error instead of a value of 'count' cannot be computed error.

Here's my case:

module "provisioner" {
  source = "path"
  hosts = "${zipmap("${google_compute_instance.consul_server.*.network_interface.0.access_config.0.assigned_nat_ip}", "${google_compute_instance.consul_server.*.network_interface.0.address}")}"

Module source:

variable "hosts" {
  type = "map"
}

resource "null_resource" "provision_consul" { 
   count = "${length(keys(var.hosts))}"   
   # etc
}

If I do this, I get:

Error configuring: 1 error(s) occurred:

* Cycle: module.provisioner.var.hosts, module.provisioner.null_resource.provision_consul (destroy), google_compute_instance.consul_server (destroy), google_compute_instance.consul_server

However, if I simply change the count in the module to a number, it works fine.

My current workaround is to declare a server_count variable in the calling template and pass it to the count attributes in both the calling template (literally) and the module (via a separate variable):

variable "server_count" {
  default = "3"
}

resource "google_compute_instance" "consul_server" {
  count = "${var.server_count}"
  ...
}

module "provisioner" {
  source = "../modules/provisioner"
  hosts = "${zipmap("${google_compute_instance.consul_server.*.network_interface.0.access_config.0.assigned_nat_ip}", "${google_compute_instance.consul_server.*.network_interface.0.address}")}"
  count = "${var.server_count}"
}

Provisioner:

resource "null_resource" "provision_consul" {
  count = "${var.count}"
  ...
}

I was just hit with a similar problem:

resource "aws_security_group_rule" "ec2_allow_ssh" {
    count             = "${var.vpc["ssh_enabled"] == "true" ? 1 : 0 }"
    type              = "ingress"

    from_port         = 22
    to_port           = 22
    protocol          = "tcp"
    cidr_blocks       = ["${split(",", var.vpc["ssh_allowed_cidrs"])}"]

    security_group_id = "${aws_security_group.nodejs_ec2.id}"
}

Throws a circular dependency error:

Errors:

  * 1 error(s) occurred:

* Cycle: module.preview_vpc.aws_subnet.public (destroy), module.preview_vpc.aws_subnet.public, module.preview_vpc.output.public_subnets, module.preview_ics-api-nodejs.var.vpc, module.preview_ics-api-nodejs.aws_security_group_rule.ec2_allow_ssh (destroy), module.preview_vpc.aws_subnet.private (destroy), module.preview_vpc.aws_subnet.private, module.preview_vpc.output.private_subnets

But if I hard code a 1 or 0, it works.

Thanks for the workaround @mfischer-zd. It's not pretty but seems we have no choice at the moment.

It look like all of the calculated parameters that influence the count should land in the terraform.tfstate state first and this can be done with manual selective terraform apply -target=.... After all dependencies are created and cached in tfstate, subsequent terraform plan does not require any manual dependency management.

I think I have a similar problem, it seems like my count worked when I had

resource "aws_route53_record" "dns" {
  count = "${length(var.domains)}"

  // ...
}

but when I added this (👇) ternary expression, it doesn't work when length(var.ips) evaluates to more than 0...

resource "aws_route53_record" "dns" {
  count = "${length(var.ips) > 0 ? length(var.domains) : 0}"

  // ...
}

We're having this issue as well, similar to the initial report (trying to pass an iam policy document into a module that references resources outside the module). If the resources already exist then it seems to work (in our staging environment the resources were created in one update and the policy added in a subsequent one, which worked), but if the resources don't exist (as we found in our production environment where both updates were applied at once) then we get the value of 'count' cannot be computed error.

commented

I'm facing a similar issue with terraform version 0.9.6 as well

The weird thing is that im using this

count = "${(signum(length(var.myVar)) + 1) % 2}"

in one module and it seems to be working fine there, the same expression in another module with another variable inside results in the value of 'count' cannot be computed error

Another thing that confuses me is that, I changed the line to

count = "${length(var.myVar) > 0 ? 0 : 1}"

And it worked for the first time after doing this, no errors everything created successfully, but after that time, doing this also results in the same error.

Is there a workaround to this?

commented

apparently I changed

count = "${length(var.myVar) > 0 ? 0 : 1}"

to (another variable)

count = "${length(var.myVar2) > 0 ? 0 : 1}"

and it is not complaining (for now)

This is still a bug / problem with 0.9.8.

Here is my version the error from a VPC module's test env:

* module.vpc.module.public-gateway.aws_route_table_association.public: aws_route_table_association.public: value of 'count' cannot be computed
* aws_instance.web: aws_instance.web: value of 'count' cannot be computed

duplicate of #10857

While I can understand the choice to not allow length() on computed values.. that choice, coupled with an inability to define module dependencies, leaves us high and dry for legit use of length():

module "nat-gateway" {
  source             = "../nat-gateways"
  vpc_id             = "${module.vpc.id}"
  name_prefix        = "${var.name_prefix}"
  nat_count          = "${length(module.private-subnets.ids)}"
  public_subnet_ids  = ["${module.public-subnets.ids}"]
  private_subnet_ids = ["${module.private-subnets.ids}"]
  extra_tags         = "${var.extra_tags}"
}

This fails if the private-subnets module hasn't already been applied.. so you need to run tf apply multiple times (using -target carefully).

Issues like this make it difficult to use Terraform modules.

Hi all! Thanks for the great discussion here.

The current approach was added as a compromise to enable the use of data source results to be used in count, as long as the data source is resolvable during the plan phase. We know that this isn't fully general and in particular doesn't work well for resource attributes, but didn't want to delay for the fully-general solution when an intermediate state was possible.

Something like #4149 will be the eventual solution to this. This sort of workflow/architecture change we want to approach carefully, but we do intend to address this problem once we've worked though the implications and found a solid design for it.

Hi @apparentlymart

What exactly is the current approch?

I'm using an external data source which massages some of my input from what is realtively easy for humans to configure, to what is relatively easy to express in terraform, and count doesn't seem to like the result of it.

My external data source only reads in data that has been passed as input variables.

i've tried this in both Terraform 0.9.9 and 0.9.11.

The current constraint is that count may only use values that can be known at plan time. There are a few ways this can be true:

  • The main way is for the count to refer directly to a data resource that doesn't have any dependency on a managed resource (a resource block in config). In this case, Terraform is able to read the data during the "refresh" step, which happens before the plan is produced, and thus the latest data is available to Terraform during the plan.
  • A more fiddly way is to use the -target option to request that Terraform should create or update a particular resource in isolation first, thus making it available in state for use as count on a subsequent run. This allows count to refer to a managed resource attribute, but requires this extra -target step first. (Automating this two-step process is what #4149 is about.)
  • Both of the above approaches can work indirectly via module variables too, with the same constraints: the value assigned to the module variable by the caller must be known at plan time, since the "known-ness" of the value propagates to the child module.

@rorybrowne if your count expression is referring only to attributes of a data resource then the first of the items above should apply, unless that data resource itself depends on a managed resource. A data resource that depends on a managed resource can't be read until the managed resource is know, so it ends up behaving like the second item above instead. If you are able to share the relevant subset of your config, I may be able to spot the reason why things aren't working as expected in your case.

Thanks @apparentlymart

I tried to reproduce it with a sample that I could send, but the reproduction didn't work. Some detective work later, it turned out that my external resource had some syntax errors.

Having that said, it would've been nice if I got a "your external data resource is fu...barred", rather than a "you can't use count" type error message. I'll see if I can recreate that problem ( unfortunatly I didn't take a commit at the time ), and do up a bug report.

@apparentlymart Thanks for taking the time to explain these proposed approaches. To me using the -target manually means that doing this at scale becomes unwieldy. Is there any update on when this is expected to be automated? Issue 4149 doesn't seem to have much activity.

I'm really baffled by the behaviour of count at this point. I'm trying to use CoreOS's kubernetes module from the Terraform registry, which has a couple of module variables, tectonic_aws_external_master_subnet_ids and tectonic_aws_external_worker_subnet_ids, which are ultimately used as part of a ternary expression in a count attribute:

data "aws_subnet" "external_worker" {
  count = "${var.external_vpc_id == "" ? 0 : length(var.external_worker_subnets)}"
  id    = "${var.external_worker_subnets[count.index]}"
}

data "aws_subnet" "external_master" {
  count = "${var.external_vpc_id == "" ? 0 : length(var.external_master_subnets)}"
  id    = "${var.external_master_subnets[count.index]}"
}

Initially I tried to pass in the output from another pre-existing module:

tectonic_aws_external_master_subnet_ids = ["${module.aws_vpc.private_subnet_ids}"]
tectonic_aws_external_worker_subnet_ids = ["${module.aws_vpc.private_subnet_ids}"]

which raises this error on plan:

* module.kubernetes.module.vpc.data.aws_subnet.external_master: data.aws_subnet.external_master: value of 'count' cannot be computed
* module.kubernetes.module.vpc.data.aws_subnet.external_worker: data.aws_subnet.external_worker: value of 'count' cannot be computed

Then I tried splitting this into two separate resources and referencing the existing module's output as remote state:

tectonic_aws_external_master_subnet_ids = ["${data.terraform_remote_state.env.aws_vpc.private_subnet_ids}"]
tectonic_aws_external_worker_subnet_ids = ["${data.terraform_remote_state.env.aws_vpc.private_subnet_ids}"]

but received the same error. And then out of frustration I tried using a literal list:

tectonic_aws_external_master_subnet_ids = ["subnet-fe5727b7", "subnet-f7c471ac", "subnet-07a1c860"]
tectonic_aws_external_worker_subnet_ids = ["subnet-fe5727b7", "subnet-f7c471ac", "subnet-07a1c860"]

And still get the same error:

* module.kubernetes.module.vpc.data.aws_subnet.external_master: data.aws_subnet.external_master: value of 'count' cannot be computed
* module.kubernetes.module.vpc.data.aws_subnet.external_worker: data.aws_subnet.external_worker: value of 'count' cannot be computed

So at this point it looks to me like you either can't use a ternary expression in a count attribute, or maybe even nothing other than an integer literal or a reference to a terraform_resource.*.id? Yet Terraform's docs give an example of using a variable in a ternary expression in a count attribute:

A common use case for conditionals is to enable/disable a resource by conditionally setting the count:

resource "aws_instance" "vpn" {
  count = "${var.something ? 1 : 0}"
}

So is this broken? Are the docs wrong? Or am I missing something? Because with this current behaviour it's not clear what the intention behind the Terraform module registry is, if passing output from one module to the input of another causes problems with basic constructs like count.

Also affected by this, it seems that lengths of lists from outputs of modules can't serve in the count parameter.

Same issue with code like

# module returns new-created from snapshot volume ID
module "volume_for_mysql" {
    source                      = "modules/create_volume_from_snapshot"
    access_key                  = "${var.access_key}"
    secret_key                  = "${var.secret_key}"
    region                      = "${var.mercury_us_region}"
    source_volume_id            = "vol-XXXX"
}
...
module "mysq" {
...
    source                      = "modules/generic_aws_instance"
    attached_volumes            = [
                                        "${module.volume_for_mysql.volume_id}"
                                  ]
    attached_volumes_count      = 1
}

Module uses code like

data "aws_ebs_volume" "disk" {
  count       = "${var.attached_volumes_count}"
# Count should be calculated here 
# like length(attached_volumes) !!!
  most_recent = true

  filter {
    name   = "volume-id"
    values = ["${var.attached_volumes[count.index]}"]
  }
}

resource "aws_volume_attachment" "disk_attachment" {
  count        = "${var.attached_volumes_count}"
  device_name  = "${var.attached_volumes_devices[count.index]}"
  volume_id    = "${element(data.aws_ebs_volume.disk.*.id, count.index)}"
  instance_id  = "${aws_instance.instance.id}"
  skip_destroy = true
}

attached_volumes_count = 1 added as workaround but I really do not like this way :(

I'm having the same issue doing something as simple as:

locals {
  users = [
    {
      name = "test"
      policy = "${data.template_file.policy.rendered}"
    }
  ]
}

resource "aws_iam_user" "all" {
  count = "${length(local.users)}"
  name = "${lookup(local.users[count.index], "name")}"
}

data "template_file" "policy" {
  template = "${file("policy.json.tpl")}"
  vars {
    resources = "blarg"
  }
}

It works if I just do a terraform plan but if I do terraform plan -target=aws_iam_user.all it doesn't work.

I was hit with this too:

variable "bastion_groups" {
        type = "list"
        default = [ "sg-12345678" ]
}

variable "bastion_sg" {
        type = "list"
        default = [ "sg-87654321" ]
}

resource "aws_security_group_rule" "ssh-bastion-in" {
        count = "${length(var.bastion_groups) * length(var.bastion_sg)}"
}

This is almost a year old, and is incredibly detrimental to module developers. Is there an update on the status of this?

So, the workaround I found was to use the newer local syntax. Apparently it ignores the limitations described by apparentlymart.

  locals {
    instance_count = "${length(module.example.instance_ids)}"
  }
  resource "null_resource" "null" {
    count = "${local.instance_count}"
  }

¯_(ツ)_/¯

UPDATE: I think you'd still run into an issue where the module value wouldn't necessarily exist until after an apply, but for my use case, I get what I need.

@artburkart THANK YOU THANK YOU THANK YOU

Hi There,

This issue remains, there does not appear to be any support for using the output variable in a module as a list type and then use length to define the count of a resource in another module. @artburkart's last comment doesn't work unless the module.example.instance_ids was already applied. This is a blocking issue that we would love to see resolved so we can build our modules out.

Terraform v0.11.2

  • provider.aws v1.3.1
  • provider.null v1.0.0
  • provider.template v1.0.0

Works

count = "${var.enable_resource == "true"? length(list("ab","bc")) : 0}"
count = "${var.enable_resource == "true"? "10" : 0}"

Does not work

count = "${var.enable_resource == "true"? length(module.name.list_var_from_output) : 0}"
count = "${var.enable_resource == "true"? "xbx" : 0}"

TF validation is awesome, but in the case above if it did not validate the ternary condition that wasn't being executed (because var.enable_resource is false) then we'd at least have a work around as dirty as it may be.

I suspect the root cause of the issue can be addressed by your awesome team members.

Our team can't really have a multi step approach to applying some resources and not others, as there's no way to enable / disable a resource or module w/o using the count directly and as noted above that won't work because the validation doesn't allow the plan if both conditions don't evaluate down to a supported variable type or value.

and now that i read the other issue, looks like mitchellh has no plans to support this right now.

Just hit this same issue as outlined above. Hoping there is some sort of work around cause right now I am blocked.

The target work around for me does not work. The reason for this is the setup I have.

  1. module that creates EC2 instances
  2. module that creates an ALB
  3. module that uses the above to modules, plus data sources and other resources.

I try to pass the instance ID from the EC2 module to the ALB module and get count cannot be computed.

The target work around doesn't work (I think) due to the fact that I have to be able to target everything but the ALB module (so the EC2 module and the additional data sources and resources)

Just ran into this issue as well.

$ terraform version
Terraform v0.11.3
+ provider.aws v1.9.0

Given a list of subnets for each availability zone:

data "aws_availability_zones" "available" {}

resource "aws_subnet" "network" {
  count = "${length(data.aws_availability_zones.available.names)}"
  availability_zone = "${data.aws_availability_zones.available.names[count.index]}"
  # …etc
}

The following examples result in a "value of 'count' cannot be computed" when trying to terraform plan

Direct attempt:

resource "aws_route_table_association" "routes" {
  count = "${length(aws_subnet.network.*.id)}"
  subnet_id = "${aws_subnet.network.*.id[count.index]}"
  route_table_id = "${data.aws_route_table.default.id}"
}

Attempt to use the locals workaround does not work:

locals {
  network_count = "${length(aws_subnet.network.*.id)}"
}

resource "aws_route_table_association" "routes" {
  count = "${local.network_count}"
  subnet_id = "${aws_subnet.network.*.id[count.index]}"
  route_table_id = "${data.aws_route_table.default.id}"
}

Edit: Reading more thoroughly into this thread, I came across the explanation that count cannot be derived from resources. Inconvenient as it may be, I can accept that explanation. The way that Terraform fails when this is the case, though, makes it very difficult for the user (such as myself) to understand what exactly went wrong. If the error messaging were improved, in the short term, to suggest that maybe the count was referencing a resource and that was not allowed… that would be very helpful.

Workaround

I was able to workaround my issue by passing around the variable used to define the number of resources I was counting. When using count, all values used in the computation must ultimately come from data sources or from variables that do not ultimately derive from a resource.

So, a minimal working example for my use case:

data "aws_availability_zones" "available" {}

resource "aws_subnet" "network" {
  count = "${length(data.aws_availability_zones.available.names)}"
  availability_zone = "${data.aws_availability_zones.available.names[count.index]}"
  # …etc
}

resource "aws_route_table_association" "routes" {
  count = "${length(data.aws_availability_zones.available.names)}"
  subnet_id = "${aws_subnet.network.*.id[count.index]}"
  route_table_id = "${data.aws_route_table.default.id}"
}

The key difference here is that although the route table here references one of the subnets, it cannot derive its count from the length of the subnets resource. Instead, it must derive its count in the same way that the subnets derive theirs. To DRY things up, this makes it attractive to extract the mutual count to a local, like so:

data "aws_availability_zones" "available" {}

locals {
  network_count = "${length(data.aws_availability_zones.available.names)}"
}

resource "aws_subnet" "network" {
  count = "${local.network_count}"
  availability_zone = "${data.aws_availability_zones.available.names[count.index]}"
  # …etc
}

resource "aws_route_table_association" "routes" {
  count = "${local.network_count}"
  subnet_id = "${aws_subnet.network.*.id[count.index]}"
  route_table_id = "${data.aws_route_table.default.id}"
}
commented

Im hitting the same issue! terraform 0.11.3-0.11.5

I am using azurerm verified module provider for compute.
as soon as i am passing a custom vm image from a datasource im hitting this problem, the resource exists, and the id is passed down to the module, so way before it even reaches that resource it should have had this item.

-target is not really a solution, cannot use this really in real life scenario

-target is not really a solution, cannot use this really in real life scenario

It may be inconvenient, but -target has been a workable solution for what I have faced here. Also, there are many ways to structure your modules and env, and it's possible to make much of this a non-issue in day-to-day operations.

commented

@ketzacoatl in a scaled solution with many pipelines this won't do, and besides im just tired of hacking and having to workaround so many bugs and constraints, i have too many of those as is.. there will come a day where all of these modifications to solve terraform's issues will hit everybody in the face because a big change will come and break all those workaround to pieces..

@pixelicous, I think you're running into the same realization my team did. Your use cases have probably outgrown the capabilities of terraform and opening new PRs doesn't fix your problems fast enough because the internal terraform team seems to lean heavily toward support for new AWS features rather than merging community PRs that fix things that should already work by now (not to say there's a PR for this particular issue). At least, this was my experience; many of my PRs would sit open for a year or more.

@pixelicous, I don't disagree with the frustrations. however, I'm focused on getting things done, and I'm simply offering a voice based on my experiences.

in a scaled solution with many pipelines this won't do,

I have worked with Terraform for several years, building out infrastructure on AWS, helping teams adopt and apply best practices, and just writing a lot of Terraform module code. ATM I am contributing to several dozen live environments, spread across a handful of different organizations. At least at the scale I'm experiencing, using -target, in conjunction with other tools and best-practices (formal processes / pipelines/etc), has scaled just fine.

and besides im just tired of hacking and having to workaround so many bugs and constraints, i have too many of those as is..

I agree that it can be "annoying to wait", but at least in my experience, with each release, the hashicorp team greatly improves my experience and the reliability of the tool while giving me minimal work to do to use the new release. For me, that's the experience I want. Tools like Terraform are ambitious projects, and take time and energy to build, so I accept that they will be flawed and I look forward to the improvements. I also need to find ways to gracefully juggle workarounds and improve / refactor those out over time.

AFAICT, (correct me if needed) this issue is blocked by other work in core that takes time to implement (the work has actually been ongoing and has spanned several major releases already), and addressing this issue (cross-module/etc dependencies) will be improved in future releases.

Another example:

resource "aws_acm_certificate" "cert" {
    domain_name                 = "${var.domain_name}"
    subject_alternative_names   = ["www.${var.domain_name}"]
    validation_method           = "DNS"
}
resource "aws_route53_record" "cert_validation" {
    count                       = "${length(aws_acm_certificate.cert.domain_validation_options)}"

    zone_id                     = "${aws_route53_zone.zone.id}"
    name                        = "${lookup(aws_acm_certificate.cert.domain_validation_options[count.index], "resource_record_name")}"
    type                        = "${lookup(aws_acm_certificate.cert.domain_validation_options[count.index], "resource_record_type")}"
    records                     = ["${lookup(aws_acm_certificate.cert.domain_validation_options[count.index], "resource_record_value")}"]
    ttl                         = 60
}
Error: Error refreshing state: 1 error(s) occurred:

* aws_route53_record.cert_validation: aws_route53_record.cert_validation: value of 'count' cannot be computed

@Z9n2JktHlZDmlhSvqc9X2MmL3BwQG7tk FWIW, the domain_validation_options is deterministic based on the number of domains + SANs in the certificate, so you could hard-code count = 2 here.

(also: 👍to the frustration of the error message being unhelpful and for the limitation of not being able to use the output of one resource to derive the count of another resource)

Getting the same kind of error in r53

count   = "${length(aws_instance.dev-ec2-loadbalancer.*.private_ip)}"
name    = "http-lb-${var.dev_vpc-base["env"]}-${format("%03d", count.index + 1)}"

Strangely enough, I was using the data source to compute the length of subnet ids that I wanted to apply to an aws network acl. It worked the first time because I'm assuming the state already had the subnet ids that it could count from. I tried the workaround with null_resourcein closed issue 7527 but it seems like you can't compute the value of the length even after triggering the network acl to start after the subnets. I'm assuming this is because it's in the state and can't be targeted. I don't want to use the terraform apply -target or try to traverse the strings from the join output back into a list for the subnet_ids to apply to the network acls. I could make the module create the subnets and apply the list during the creation of the network acl but I haven't tried that yet.

Looks like this is largely a limitation of the HCL libraries. It it supposed to be fixed in the next major release. See #18015

About 4 hours into terraform and been a module developer for about 3 of those hours and already hit by comments saying I have out grown terraform.... oh dear.

Working around this by hardcoding the count and passing it through to the module for now.

Hit this issue as well trying to do

count = "${length(aws_subnet.public_subnet.*.id)}"

Worked around by setting a static value for count for the time being.

commented

@artburkart Exactly, its just horrible, at least we aren't in production yet, im terrified from this day with terraform, the amount of issues we are experiencing is vast!

@steve-genilogic i've been able to omit this like this:
variables.tf

variable "private_subnet_cidr_block" {
  type = "list"
  default = ["10.10.6.0/25","10.10.6.128/25","10.10.8.0/25","10.10.8.128/25"]
  description = "Private Subnet CIDR Block"
}

then in aws_subnet resource,
i used count = "${length(var.private_subnet_cidr_block)}"

I encountered same error when using with aws_route_table_association

resource "aws_default_route_table" "route" {
  default_route_table_id = "${var.main_route_table_id}"
}

resource "aws_route_table_association" "route_ass" {
  count          = "${length(var.subnets)}"
  subnet_id      = "${element(var.subnets, count.index)}"
  route_table_id = "${var.main_route_table_id}"
}

Workaround was provided by @canterberry

data "aws_availability_zones" "available" {}

locals {
  network_count = "${length(data.aws_availability_zones.available.names)}"
}

resource "aws_default_route_table" "route" {
  default_route_table_id = "${var.main_route_table_id}"
}

resource "aws_route_table_association" "route_ass" {
  count          = "${local.network_count}"
  subnet_id      = "${element(var.subnets, count.index)}"
  route_table_id = "${var.main_route_table_id}"
}

Waiting for #4149, would it be possible to report the expression that cannot be resolved in the error message? It would be very helpful!

getting rid of length() doesn't seem to resolve it either.
fails:
count = "${element(concat(var.sqs_queues, list("")),0) != "" ? 1 : 0}"

I am too experiencing this error when trying to convert the current configuration to the module.

I.e. reusing current configuration as a module in other configuration and defining all the variables in module declaration (instead of specifying them on the command line)

Another note about the issue.

data "aws_ssm_parameter" "count" {
  name = "${var.ssm_path}/count"
}
resource "aws_elasticache_replication_group" "main" {
  count = "${data.aws_ssm_parameter.count.value}"
  // ...
}

This works on the root module, but I get the value of 'count' cannot be computed when included (i.e. non root module).

commented

Amazingly this is open for over a year now 🤦‍♂️

I just hit this as well... Modules become more and more useless for me. Every time I try putting my code into a module, after a few hours I'm reading github issues that say "sorry, that's not possible". (just as a few examples: using count with modules, using depends_on with modules and now this). Calling hardcoding count a workaround is like a programming language telling the programmer "sorry, we don't support loops, just copy&paste your code as often as you want to run it"

@Crapworks I use count with modules in almost all of my infrastructure. You just have to use one of the listed workarounds.

Terraform 0.12 will fix all these problems. Due to be released soon.

Hi all!

By now this issue is covering quite a number of different root problems that all happen to have the same symptoms. The root problem that Terraform cannot support a computed count will unfortunately remain true until #4149 can be addressed in a later release, but in the v0.12-alpha1 the most common cases where Terraform would unnecessarily consider a value to be "computed" have been addressed.

For example, several people in this thread were (understandably) surprised that Terraform would consider an expression like length(aws_instance.foo.*.id) to be computed, since Terraform ought to know how many instances there are of aws_instance.foo. In Terraform v0.12.0-alpha1 I've verified that this is indeed now working as expected:

resource "null_resource" "a" {
  count = 2
}

resource "null_resource" "b" {
  count = length(null_resource.a)
}

output "ids" {
  value = null_resource.b.*.id
}
$ terraform apply

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # null_resource.a[0] will be created
  + resource "null_resource" "a" {
      + id = (known after apply)
    }

  # null_resource.a[1] will be created
  + resource "null_resource" "a" {
      + id = (known after apply)
    }

  # null_resource.b[0] will be created
  + resource "null_resource" "b" {
      + id = (known after apply)
    }

  # null_resource.b[1] will be created
  + resource "null_resource" "b" {
      + id = (known after apply)
    }

Plan: 4 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

null_resource.a[0]: Creating...
null_resource.a[1]: Creating...
null_resource.a[1]: Creation complete after 0s [id=8551500515924767906]
null_resource.a[0]: Creation complete after 0s [id=1112095521491468891]
null_resource.b[0]: Creating...
null_resource.b[1]: Creating...
null_resource.b[1]: Creation complete after 0s [id=8431205028446970269]
null_resource.b[0]: Creation complete after 0s [id=1368916686735775390]

Apply complete! Resources: 4 added, 0 changed, 0 destroyed.

Outputs:

ids = [
  "1368916686735775390",
  "8431205028446970269",
]

Note that the terminology "computed" has now been replaced with "unknown" in v0.12, with unknown values showing as "(known after apply)" in the rendered diff.

Along with this specific case, a list or map that contains an unknown element can now pass through a module boundary (in via a variable or out via an output) as-is, without the entire collection itself becoming unknown as we saw in v0.11 and prior. The appearance of this error should therefore be in a much smaller set of cases than before.


The v0.12.0-alpha1 also includes a more detailed error message for the remaining cases where count is unknown.

In a future minor release we also intend to make this do one more level of analysis to find the specific reference that is unknown and indicate it directly, ideally also then offering an exact -target option value that would resolve it, but sadly we were not able to fit the remaining work for that into the initial v0.12.0 release scope due to unexpected extra complexity elsewhere. In the mean time, we hope that this new error message at least gives some better context about the problem and some broad guidance on where to start with working around it until #4149 is resolved, which must wait for a subsequent major release due to the non-trivial graph builder adjustments it requires.

I have figured out a workaround for 0.11.x. I create a list of resources IDs, which are dynamically computed, and then I attach a local provisioner to the dependent resource which just runs a bash comment with the interpolated list. Example follows:

# Compute dynamic dependencies for RKE provisioning step (workaround, may be not needed in 0.12)
locals {
  rke_cluster_deps = [
    "${join(",",module.master.prepare_nodes_id_list)}",
    "${join(",",module.worker.prepare_nodes_id_list)}",
    "${join(",",module.master.associate_floating_ip_id_list)}",
    "${join(",",module.master.allowed_ingress_id_list)}",
    "${join(",",module.worker.allowed_ingress_id_list)}",
    "${join(",",module.secgroup.rule_id_list)}",
    "${module.network.interface_id}",
  ]
}

# Provision RKE
resource rke_cluster "cluster" {
  nodes_conf = ["${concat(module.master.node_mappings,module.worker.node_mappings)}"]

  bastion_host = {
    address      = "${element(module.master.public_ip_list,0)}"
    user         = "${var.ssh_user}"
    ssh_key_path = "${var.ssh_key}"
    port         = 22
  }

  ignore_docker_version = "${var.ignore_docker_version}"

  # Workaround: make sure resources are created and deleted in the right order
  provisioner "local-exec" {
    command = "# ${join(",",local.rke_cluster_deps)}"
  }
}

# Write kubeconfig.yaml
resource local_file "kube_cluster_yaml" {
  filename = "./kubeconfig.yml"
  content  = "${rke_cluster.cluster.kube_config_yaml}"
}

I do this mostly because I want rke_cluster to be dependent on resources inside modules, but you can compute other stuff and attach it in the same way.

Hey Mcapuccini, I'm also using rancher and trying (hard) to create some valid and dynamic yaml (I'm trying to use hostname). I'm not using ansible so my config may differ from yours. Can you offer any support at all?

Hi @tim-baker115. I got it working eventually: https://github.com/mcapuccini/terraform-openstack-rke. It's still work in progress, but feel free to give it a try. If you have any problem you can come back to me via issue. I am not creating the YAML, but I am using: https://github.com/yamamoto-febc/terraform-provider-rke.

For me I am literally using the example the terraform doc uses and it is failing with the "count can not be computed error":

data "aws_subnet_ids" "existing" {
  vpc_id = "${var.udb_vpc}"
}

data "aws_subnet" "computed" {
   count = "${length(data.aws_subnet_ids.existing.ids)}"
   id    = "${data.aws_subnet_ids.existing.ids[count.index]}"
}

resource "aws_elb" "udb_elb" {
  name            = "${var.role}-${var.mode}-${var.version}"
  internal        = false
  security_groups = ["${aws_default_security_group.default.id}", "${aws_security_group.udb_service_access.id}", "${aws_security_group.udb_frontend_access.id}"]
  subnets         = ["${data.aws_subnet.computed.ids}"]

  listener {
    instance_port     = "${var.api_port}"
    instance_protocol = "TCP"
    lb_port           = "${var.api_port}"
    lb_protocol       = "TCP"
  }

  listener {
    instance_port     = "${var.frontend_port}"
    instance_protocol = "TCP"
    lb_port           = "${var.frontend_port}"
    lb_protocol       = "TCP"
  }

  health_check {
    healthy_threshold   = 3
    unhealthy_threshold = 3
    timeout             = 5
    target              = "TCP:${var.frontend_port}"
    interval            = 30
  }

  cross_zone_load_balancing   = true
  idle_timeout                = 60
  connection_draining         = true
  connection_draining_timeout = 300
}

@apparentlymart I'm hitting the symptoms described in this issue, but I'm not sure the root cause is the same as what the workarounds suggested above indicate.

Given the following config:

resource "azurerm_app_service" "backend" {
    # omitted for brevity
}

resource "azurerm_postgresql_server" "transferapi-db" {
    # omitted for brevity
}


locals {
  db_ip_whitelist = ["${split(",", azurerm_app_service.backend.possible_outbound_ip_addresses)}"]
}

resource "azurerm_postgresql_firewall_rule" "db-backend" {
  count = "${length(local.db_ip_whitelist)}"
  name  = "backend"

  depends_on = ["azurerm_app_service.backend", "azurerm_postgresql_server.db"]

  resource_group_name = "${azurerm_resource_group.backend.name}"
  server_name         = "${azurerm_postgresql_server.db.name}"
  start_ip_address    = "${element(local.db_ip_whitelist, count.index)}"
  end_ip_address      = "${element(local.db_ip_whitelist, count.index)}"
}

I expect to fail in the ways mentioned above when the app service is not yet provisioned. However, I'm seeing this error even after first creating the app service using -target=.

I can also verify that the property is indeed known and present in my state, by looking at

$ terraform state show azurerm_app_service.backend | grep possible
possible_outbound_ip_addresses                   = <a redacted comma-separated list of addresses>

So if this is present in my state, why can count not be computed?

I've also tried inlining the expression instead of having it in a local variable, but that made no difference.

yesterday, terraform v0.12 is announced, but seems still doesn't support count in module.

Is it true?

@ozbillwang if you're looking for count in modules then check this issue #953. Its not in 0.12.

Hi all,

Per the earlier comment from @apparentlymart, Terraform 0.12 includes an improved error message for this situation that includes a more detailed description of the problem and a specific suggestion for how you might work around it.

In the long term we'd like to make Terraform handle this situation automatically by automatically excluding the necessary resources from the initial plan, but that is already covered by #4149 and so we're going to close this one now to consolidate the discussion over there. Thanks for the great discussion here!

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.