Terraform: Feature Request: Defining variables/locals inside a resource block

Created on 13 Mar 2019  路  7Comments  路  Source: hashicorp/terraform

I trying to define some variables inside a resource so that it can be referred when needed and this resource block is in the loop so I can't define these as locals outside the resource.
Is this possible in some way?:

resource "aws_cloudwatch_metric_alarm" "httpcode_target_5xx_count" {
  locals {
      len = "${length(data.aws_lb.this.target_group_arn_suffixes)}"
      current_index = "${element(data.aws_lb.this.target_group_arn_suffixes, count.index)}"
      current_thresold = "${var.target_5xx_thresold["$local.current_index"] != "" ? var.target_5xx_thresold["$local.current_index"] : "${var.lb_5xx_thresold}"}"
  }
  count                     = "${local.len}"
  alarm_name                = "${var.prefix_resources}: ALB-5XX-ERROR: TARGET: ${replace(local.current_index,"/(targetgroup/)|(/\\w+$)/","")}"
  comparison_operator       = "GreaterThanThreshold"
  evaluation_periods        = "${local.evaluation_periods}"
  metric_name               = "HTTPCode_Target_5XX_Count"
  namespace                 = "AWS/ApplicationELB"
  period                    = "300"
  statistic                 = "Sum"
  threshold                 = "${local.current_thresold}"
  treat_missing_data        = "missing"
  alarm_description         = "HTTPCode 5XX count for ${local.current_index} over ${local.current_thresold} last 300 seconds over 1 period(s)"
  alarm_actions             = "${concat(var.notify_sns_arns, aws_sns_topic.opsgenie_sns_topic.arn)}"
  dimensions = {
    "TargetGroup"  = "${local.current_index}"
    "LoadBalancer" = "${data.aws_lb.this.arn_suffix}"
  }
}

Thank you very much, i tried searching but couldn't got anything hence a github issue. Tried a IRC (#terraform-tool) also but it seems that nobody was there.

config enhancement

Most helpful comment

I'll add myself to the list of those who would like to have this feature implemented 馃敡

All 7 comments

Hi @rahulwa,

That isn't possible in Terraform today. For Terraform v0.12 (in the master branch) we've actually reserved the name locals in this context to allow us to potentially implement that feature in the future, since indeed it would be very useful for the reasons you state here, but we've not yet been able to implement that feature.

If it's okay with you, I'd like to turn this issue into a feature request for that, because I don't think we have one open already. That'll give a place for other users to vote for the feature (by adding :+1: reactions to your original message, which we can use as an input for prioritization).


For this _particular_ use-case, we are also planning to add a new for_each feature to Terraform, described in #17179, which once implemented would at least remove the need for your len and current_index values because the built-in feature would manage those for you:

resource "aws_cloudwatch_metric_alarm" "httpcode_target_5xx_count" {
  for_each = {
    for s in data.aws_lb.this.target_group_arn_suffixes : s => {
      threshold = lookup(var.target_5xx_threshold, s, var.lb_5xx_threshold)
    }
  }

  alarm_name                = "${var.prefix_resources}: ALB-5XX-ERROR: TARGET: ${replace(each.key ,"/(targetgroup/)|(/\\w+$)/","")}"
  comparison_operator       = "GreaterThanThreshold"
  evaluation_periods        = local.evaluation_periods
  metric_name               = "HTTPCode_Target_5XX_Count"
  namespace                 = "AWS/ApplicationELB"
  period                    = "300"
  statistic                 = "Sum"
  threshold                 = each.value.threshold
  treat_missing_data        = "missing"
  alarm_description         = "HTTPCode 5XX count for ${local.current_index} over ${local.current_thresold} last 300 seconds over 1 period(s)"
  alarm_actions             = concat(var.notify_sns_arns, aws_sns_topic.opsgenie_sns_topic.arn)
  dimensions = {
    "TargetGroup"  = each.key
    "LoadBalancer" = data.aws_lb.this.arn_suffix
  }
}

This feature also won't be fully complete in v0.12.0 due to the already-large scope of that release, but it's closer to being completed and we're hoping to complete it shortly after in a subsequent release.

One reason we've de-prioritized the ability to use locals inside resource blocks for now is that we suspect that the for_each feature may meet the same use-cases in a simpler way, so we want to release that first and see how it is used to see if it can cover all of the use-cases or if a resource-specific locals block would be helpful for other reasons.


For now, unfortunately inserting the full expressions into the attributes directly is the only answer for current Terraform releases. We know this hurts readability and maintainability, so these other planned features are the intended answer to that once we are able to complete the other foundational work that is needed to support them.

@apparentlymart You can go ahead and create it as a feature request and thank you very much for replying so promptly. so, for now, I will be going forward with normal flow.
I also think that for_each is very important for readability so 馃憤 馃挴 for this feature.

The feature described here is likely to be the solution for #3267. I'm not going to close either of them as a duplicate of the other for now, since this one is a description of a solution while the other is a description of a use-case and the other has a lot of votes attached to it that I don't want to lose, but this comment should help us track the relationship here so we can update them both together in future.

I would also very much like to see this. Locals have been a big improvement, but I find myself using them a lot for look-up tables that would be much cleaner if the values were simply attached to the resource that the key references, as suggested here.

I'll add myself to the list of those who would like to have this feature implemented 馃敡

Just to address the question of whether the resource locals block will still be useful after for_each is working: I'm using for_each in a resource, but would still find a locals block inside the resource very useful for cleaning up the resource by adding some data translation (applying functions to pieces of each.value, and re-using the result) inside each instance of the resource without implementing another level of modules just to hold the translated data. Great feature! 馃憤 馃檹

Hi all!

I just wanted to clarify what I meant in my earlier comment about how the for_each feature might solve a bunch of the use-cases. Since you can use for expressions to produce a transformed map, you can in principle sneak some extra expressions in there for any transformations you might want to re-use multiple times.

Here's a contrived example because it's late in my day and I'm lacking imagination. :grinning:

variable "names" {
  type = set(string)
}

resource "example" "example" {
  for_each = {
    for n in var.names : n => {
      # The attributes defined in here will be available via each.value
      name_upper = upper(name)
      name_lower = lower(name)
    }
  }

  name_as_given = each.key
  name_upper    = each.value.name_upper
  name_lower    = each.value.name_lower
}

The elements of the for_each collection effectively create a local scope containing each.key and each.value on each repetition, and each.value is entirely under your control because Terraform doesn't use the map values for any purpose. (each.key is more constrained because it also becomes the tracking key for the instances.)

A similar approach can also work for dynamic blocks when nested block repetition is the goal, because the for_each in there behaves in a similar way.

I don't mean to imply that the above necessarily solves all the problems that a local locals would solve, but I wanted to share it in case it's useful to folks with goals like this in the meantime, since resource-level for_each was implemented in Terraform v0.12.6. (I _would_ love to hear about anything you come up with that _would_ require the first-class locals block I described in my earlier comment, though!)

Was this page helpful?
0 / 5 - 0 ratings