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.
https://www.terraform.io/
Other
42.29k stars 9.48k forks source link

Feature Request: Make locals a code block #26303

Open nicolas-lopez opened 3 years ago

nicolas-lopez commented 3 years ago

Use-cases

Hello I often use locals to define variables on the fly and it's currently a pain that loops and condition can only be used inline as an expression value.

Here is an example:

locals {
  customs = flatten([for group in keys(aws_iam_group.groups): [for policy in values(aws_iam_policy.customs): {group=group, policy_arn=policy.arn} if split("-", policy.name)[0] == group]])
  custom_policies_map = zipmap(range(length(local.customs)), local.customs)
}

resource "aws_iam_group_policy_attachment" "customs" {
  for_each   = local.custom_policies_map

  group      = each.value.group
  policy_arn = each.value.policy_arn
  depends_on = [aws_iam_group.groups, aws_iam_policy.customs]
}

Second limitation of locals is that you can't re-assign a new value to a defined local as you can see in the code above I have to use two local in order to make it a bit more readable (laugh in terraform locals).

Proposal

In my opinion a good solution would be to make locals block a code block in which you can specify variable to return in the global scope. It also would bring more possibilities to the HCL by introducing a entire new set of feature imported from coding itself like functions.

Here is a dummy example of what it could look like:

locals {
  # This is not an returned variable
  foo = ["bar", "baz"]

  # This is a returned variable
  local my_var = ""

  # This is a loop and a condition
  for k in foo {
    if (k == "bar") {
      my_var = concat(my_var, k)
    } else {
      my_var = my_var
    }
  }
}
apparentlymart commented 3 years ago

Hi @nicolas-lopez! Thanks for sharing this feature request.

What you've proposed here effectively amounts to changing the basic paradigm of the Terraform language from declarative/functional to imperative, at least inside the locals block here. The Terraform language runtime is not designed to evaluate this sort of imperative code (it totally lacks mutable variables, for example), so we'd essentially be building an entirely new language embedded inside the existing Terraform language.

While such a thing would be technically possible, I think at that point folks would reasonably ask why we'd built what is effectively a new imperative scripting language rather than using one that already exists.

There are some existing options available that seem like they could achieve similar use-cases today, in increasing order of "distance from typical Terraform usage":

Of all of these, the apparentlymart/javascript provider seems like the closest to what you are proposing, in that it embeds a small imperative program inside an otherwise-declarative Terraform module. Perhaps you could try implementing something like your local.customs and local.custom_policies_map example with that provider and see how it works out, and that'll hopefully help to decide whether there's more to do in order to meet the use-case you've shared.

Thanks again!

nicolas-lopez commented 3 years ago

Hello after reading your answer I though wouldn't it be possible to add a block for CDK directly into terraform to get a json object that we could parse in the HCL. This block would help with dynamic configuration without breaking to much of the static processing.

On the other hand I have bit of struggle understand that we are trying to achieve "infrastructure as code" but HCL is not a coding language at all.

Thanks for your answer and the options you mentioned, I will investigate them to see if they can fit my use case =).

Regards