# Translate locals + interpolation into variables form

**URL:** <https://discuss.hashicorp.com/t/translate-locals-interpolation-into-variables-form/24609>\
**Category:** Terraform\
**Created:** [May 20, 2021, 11:59pm UTC](https://discuss.hashicorp.com/t/translate-locals-interpolation-into-variables-form/24609 "2021-05-20T23:59:00Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![Roxyrob](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/roxyrob/32/11129_2.png) [@Roxyrob](https://discuss.hashicorp.com/u/Roxyrob)\
**Post date:** [May 20, 2021, 11:59pm UTC](https://discuss.hashicorp.com/t/translate-locals-interpolation-into-variables-form/24609/1 "2021-05-20T23:59:00Z")

</div>

Terraform does not support interpolation in variables, so I have been able to create dns records using reference to a module (e.g. module.eip…) using locals:

```auto
locals {

  route53_hz_records = {
    "inc.example.net" = [
      {
        "name" = "www"
        "type" = "A"
        "ttl" = 3600
        "records" = [module.eip.eip["pubip1"].public_ip ]
      },
    ]
  }

}

```

How can be refactored this code to be able to use variables (e.g to allow different records configuration base on different environment - specific tfvars file based on env) ?

ps: `module "eip" {...}` will be created inside same terrafom configuration.

---

<div class="post-metadata">

**Author:** ![apparentlymart](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/apparentlymart/32/702_2.png) [@apparentlymart](https://discuss.hashicorp.com/u/apparentlymart)\
**Post date:** [May 21, 2021, 11:33pm UTC](https://discuss.hashicorp.com/t/translate-locals-interpolation-into-variables-form/24609/2 "2021-05-21T23:33:47Z")

</div>

Hi @Roxyrob,

I think I might not be understanding correctly what you want to achieve, but the typical answer to letting something be specified by the calling module is to declare an input variable for it like this:

```auto
variable "route53_hz_records" {
  type = map(object({
    name = string
    type = string
    tty = number
    records = list(string)
  }))
}

```

Each of your per-environment configurations can then call this module using different values for that variable::

```auto
module "eip" {

}

module "example" {
  # ...

  route53_hz_records = {
    "inc.example.net" = [
      {
        "name" = "www"
        "type" = "A"
        "ttl" = 3600
        "records" = [module.eip.eip["pubip1"].public_ip ]
      },
    ]
  }
}

```

The differences in which records are created per environment would therefore come from the differences between the configurations for each of those environments.

---

<div class="post-metadata">

**Author:** ![Roxyrob](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/roxyrob/32/11129_2.png) [@Roxyrob](https://discuss.hashicorp.com/u/Roxyrob)\
**Post date:** [May 22, 2021, 1:01am UTC](https://discuss.hashicorp.com/t/translate-locals-interpolation-into-variables-form/24609/3 "2021-05-22T01:01:13Z")

</div>

Hi @apparentlymart,  
the complete config sample below.

I use terraspace to complement terraform. Terraspace allow to define variables specific for environments (creating a target terraform dir with spec tfvars file for environment and running terraform inside) so I might have different `route53_hz_records_persist` (and so different number or type of Route53 records) defined per environment in a specific tfvars file (devel.tfvars, prod.tfvars, …) but cannot do that as terraform does not support interpolation in variables (in this case for late subtitution of module.eip…), so I use below configuration (that works), but locals impose same config for every environment.

Not sure if I can overcome this limitation and/or if this is the right way !?

```auto
locals {

  ### ROUTE 53 HOSTED ZONES RECORDS
  route53_hz_records_persist = {
    "inc.example.net" = [
      {
        "name" = "www"
        "type" = "A"
        "ttl" = 3600
        "records" = [
          module.eip.eip["pubip3"].public_ip,
        ]
      },
      {
        "name" = "service"
        "type" = "A"
        "ttl" = 3600
        "records" = [
          module.eip.eip["pubip3"].public_ip,
        ]
      },
      {
        "name" = "anpr"
        "type" = "A"
        "ttl" = 3600
        "records" = [
          module.eip.eip["pubip4"].public_ip,
        ]
      },
    ]
  }
}

module "eip" {
  source = "..."
  ...
}

module "route53_hz_root_records" {
  source = "..."

  for_each = local.route53_hz_records_persist

  zone_name = each.key
  records = each.value
}

```

---

<div class="post-metadata">

**Author:** ![apparentlymart](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/apparentlymart/32/702_2.png) [@apparentlymart](https://discuss.hashicorp.com/u/apparentlymart)\
**Post date:** [May 24, 2021, 6:36pm UTC](https://discuss.hashicorp.com/t/translate-locals-interpolation-into-variables-form/24609/4 "2021-05-24T18:36:50Z")

</div>

Hi @Roxyrob,

I don’t think there’s any _general_ answer to this because if you want to use arbitrary Terraform language expressions then you need to write your value in the Terraform language, not externally as an input variable.

However, if you can put some more constraints on the problem then you could potentially define an indirect approach for describing the intended shape and then derive the real final data structure, including the appropriate dynamically-chosen values, from that higher-level input.

For example:

```auto
variable "dns_records" {
  type = map(set(object({
    name = string
    ttl = number
    target_key = string
  })))
}

locals {
  dns_records = {
    for domain, records in var.dns_records : domain => {
      for record in records : {
        name = record.name
        type = "A"
        ttl = record.ttl
        records = [
          module.eip.eip[record.target_key].public_ip
        ]
      }
    }
  }
}

```

The above example assumes that all of your records will refer to exactly one of the available elastic IP addresses using its key. This is just an example constraint to show what I mean; even if that’s not an appropriate constraint for your case, hopefully this example serves to illustrate that you can use a static value as a definition for how to construct a dynamic one, and thus allow the overall structure to be defined statically as a variable while the final values will come dynamically from the main configuration.

---

<div class="post-metadata">

**Author:** ![Roxyrob](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/roxyrob/32/11129_2.png) [@Roxyrob](https://discuss.hashicorp.com/u/Roxyrob)\
**Post date:** [May 29, 2021, 1:41am UTC](https://discuss.hashicorp.com/t/translate-locals-interpolation-into-variables-form/24609/5 "2021-05-29T01:41:32Z")

</div>

Thank you @apparentlymart. It’s a good solution for many cases. I’ll try to adapt to my config.

---

<div class="post-metadata">

**Author:** ![Roxyrob](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/roxyrob/32/11129_2.png) [@Roxyrob](https://discuss.hashicorp.com/u/Roxyrob)\
**Post date:** [July 29, 2021, 5:00pm UTC](https://discuss.hashicorp.com/t/translate-locals-interpolation-into-variables-form/24609/6 "2021-07-29T17:00:56Z")

</div>

Thank you @apparentlymart,  
approach you shown me, with some small corrections

```auto
 domain => [

```

instead of

```auto
 domain => {

```

is ok for my case. I just use eip referenced by name creating from a list like this

```auto
pub_ips = ["pubip1", "pubip2", "pubip3", "pubip4", ...]

```

Please, if you can, explane me better what you mean with your initial sentence I cannot understand completely

```

... because if you want to use arbitrary Terraform language expressions
then you need to write your value in the Terraform language, not externally
as an input variable.

```

And **also what about contraint you pointed to** (I cannot see as I can choose different pubip using target\_key for each record), see code below:

**(local variable)**

```auto
local {
  route53_hz_records_local = {
    for domain, records in var.route53_hz_records : domain => [
      for record in records : {
        name = record.name
        type = record.type
        ttl = record.ttl
        records = [
          module.eip.eip[record.target_key].public_ip
        ]
      }
    ]
  }
}

```

**(variables declaration)**

```auto
variable "pub_ips" {
  type = list(string)
  default = []
}

variable "route53_hz_records" {
  type = map(set(object({
    name = string
    type = string
    ttl = number
    target_key = string
  })))
  default = {}
}

```

**(variables definition)**

```auto
pub_ips = ["pubip1", "pubip2", "pubip3", "pubip4"]

route53_hz_records = {
  "domain1.example.com" = [
    {
      "name" = "www"
      "type" = "A"
      "ttl" = 3600
      "target_key" = "pubip1"
    },
    {
      "name" = "service1"
      "type" = "A"
      "ttl" = 3600
      "target_key" = "pubip3"
    },
    {
      "name" = "service2"
      "type" = "A"
      "ttl" = 3600
      "target_key" = "pubip4"
    },
  ]
}

```

**(eip)**

```auto
module "eip" {
  source = "..."
  ...
}

```

**(records)**

```auto
module "route53_hz_root_records" {
  source = "..."

  for_each = {
    for domain, records in local.route53_hz_records_local :
    domain => records
  }

  zone_name = each.key
  records = each.value
}

```
