Terraform: Value of count cannot be computed when using a map containing a reference to a resource

Created on 4 Jul 2017  路  6Comments  路  Source: hashicorp/terraform

Terraform Version

Terraform v0.9.11

Problem description

We are creating a module to set up AWS cloudwatch alarms. The module accepts a map containing the configuration for each of the alarms that users want to configure (var.alarms). Then we instantiate multiple aws_cloudwatch_metric_alarm resources using count.

Everything works fine until var.alarms contains a reference to another resource. In the example, we use the ARN for an SNS topic, but we have tried other resources such as the deployment ARN and it fails as well.

The error message is:

module.api_cloudwatch_alarms.aws_cloudwatch_metric_alarm.alarm: aws_cloudwatch_metric_alarm.alarm: value of 'count' cannot be computed

Terraform Configuration Files

main.tf

variable "profile" {
  default = ""
}

variable "region" {
  default = ""
}

provider "aws" {
  profile    = "${var.profile}"
  region     = "${var.region}"
}

resource "aws_api_gateway_rest_api" "api" {
  name = "api"
}

resource "aws_api_gateway_deployment" "deployment" {
  rest_api_id = "${aws_api_gateway_rest_api.api.id}"
  stage_name  = "test"
}

module "api_cloudwatch_alarms" {
  source = "./api_cloudwatch_monitors"

  api_name   = "${aws_api_gateway_rest_api.api.name}"
  stage_name = "test"
  alarms = {
    "4XXError" = {
      actions = "${aws_sns_topic.topic.arn}"
      threshold = 10
    }
  }
}

resource "aws_sns_topic" "topic" {
  name = "topic"
}

api_cloudwatch_monitors/main.tf

variable "api_name" {}

variable "stage_name" {}

variable "alarms" {
  type = "map"
  default = {}
}

resource "aws_cloudwatch_metric_alarm" "alarm" {
  count = "${length(var.alarms)}"
  alarm_name = "${format("%s-%s-%s",
                  var.api_name,
                  var.stage_name,
                  element(keys(var.alarms), count.index))}"
  comparison_operator = "GreaterThanOrEqualToThreshold"
  evaluation_periods = 2
  metric_name = "${element(keys(var.alarms), count.index)}"
  namespace = "AWS/ApiGateway"
  period = 100
  threshold = 100

  alarm_actions = ["${split(",", lookup(
                    var.alarms[element(keys(var.alarms), count.index)],
                    "actions",
                    ""
                  ))}"]
}

Debug Output

https://gist.github.com/betabandido/e4fe821a5d08b1a291e508c7a163c4eb

config enhancement

Most helpful comment

This appears to be a massive limitation - "count" appears to be a necessity in reducing the amount of code/text written which helps to keep the code maintainable as it reduces having to duplicate code unnecessarily. I have use of count only to prevent resources in modules from being "run" but "count cannot compute" comes up and scuppers what i wrote.

Using target in a wrapper script once is fine but more than once is often a headache and error prone especially when trying to write "sane" non-complex DRY terraform

This is a Big limitiation

All 6 comments

@betabandido this is expected because since the value of "${aws_sns_topic.topic.arn}" is not know during the plan/apply phase, terraform assumes the whole map as an un-computed resource.

main.tf

variable "region" {
    default = "us-east-1"
}

provider "aws" {
    region = "${var.region}"
}

resource "aws_sns_topic" "topic" {
  name = "topic"
}

module "foo" {
    source = "./module/"

    alarms = {
        "4XXError" = {
            actions = "${aws_sns_topic.topic.arn}" does not work
            # actions = "foo" works
            # actions = "arn:aws:sns:<region>:<account>:<topic_name>" works!
            threshold = 10
        }
    }
}

module/main.tf

variable "alarms" {
  type = "map"
  default = {}
}

resource null_resource "foo" {
    count = "${length(keys(var.alarms))}"
    provisioner "local-exec" {
        command = "echo ${count.index}"
    }
}

@apparentlymart gave me a good explanation a while ago. Here you go!

@betabandido by building the sns topic you don't need to use the extra variable you used here

Isn't it possible to use some sort of lazy evaluation? As the keys in the map are plain strings, their number does not depend on the potential contents of the map values. Therefore, length(keys(var.alarms)) could easily work.

If this is just impossible, I suppose an alternative is to first create the SNS topic using -target. Is that correct?

@betabandido yep using -target to create the SNS first and then the alarms should work. Since our CI server performs the terraforming, it is inconvenient for us to add -target everywhere and so I build the arn wherever possible.

I'm hitting the same error following the sample in the documentation - is this not valid? https://www.terraform.io/docs/configuration/interpolation.html#using-templates-with-count

This appears to be a massive limitation - "count" appears to be a necessity in reducing the amount of code/text written which helps to keep the code maintainable as it reduces having to duplicate code unnecessarily. I have use of count only to prevent resources in modules from being "run" but "count cannot compute" comes up and scuppers what i wrote.

Using target in a wrapper script once is fine but more than once is often a headache and error prone especially when trying to write "sane" non-complex DRY terraform

This is a Big limitiation

Hitting the same issue. Would be glad to see it fixed.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

gwagner picture gwagner  路  81Comments

kforsthoevel picture kforsthoevel  路  86Comments

FlorinAndrei picture FlorinAndrei  路  61Comments

dupuy picture dupuy  路  61Comments

bloopletech picture bloopletech  路  82Comments