# How to interpolate in Terraform

**URL:** <https://discuss.hashicorp.com/t/how-to-interpolate-in-terraform/11020>\
**Category:** Terraform\
**Created:** [July 4, 2020, 12:08pm UTC](https://discuss.hashicorp.com/t/how-to-interpolate-in-terraform/11020 "2020-07-04T12:08:00Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![iamdempa](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/iamdempa/32/5491_2.png) [@iamdempa](https://discuss.hashicorp.com/u/iamdempa)\
**Post date:** [July 4, 2020, 12:08pm UTC](https://discuss.hashicorp.com/t/how-to-interpolate-in-terraform/11020/1 "2020-07-04T12:08:00Z")

</div>

Hi,  
I am trying to execute a small bash command with `local-exec` in `terraform`

But I need to do the interpolation

This is how my code looks like:

```auto
resource "null_resource" "web3" {

 triggers = {
    key = "${uuid()}
  provisioner "local-exec" {
    command = "num1=${var.minion-count} && num2=1 && num3=$[num1 - num2] && for i in $(seq 0 $num3); do echo ${aws_instance.kubernetes_minion.<value-of-i>.public_ip} > /etc/ansible/machan; done"
   
  }
 }

```

I need to pass the value of `i` to where it says “value-of-i”, so I did as below

```auto
resource "null_resource" "web3" {

 triggers = {
    key = "${uuid()}
  provisioner "local-exec" {
    command = "num1=${var.minion-count} && num2=1 && num3=$[num1 - num2] && for i in $(seq 0 $num3); do echo ${aws_instance.kubernetes_minion.$i.public_ip} > /etc/ansible/machan; done"
   
  }
 }

```

And give the error:

```
Expected the start of an expression, but found an invalid expression token.

```

Can someone please help me how to properly do this?

---

<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:** [July 6, 2020, 6:39pm UTC](https://discuss.hashicorp.com/t/how-to-interpolate-in-terraform/11020/2 "2020-07-06T18:39:41Z")

</div>

Hi @iamdempa,

It looks like there are several syntax errors in your configuration even before you introduced the `$i` interpolation to the shell string. Here’s my attempt to fix the syntax of your first example:

```auto
resource "null_resource" "web3" {
  triggers = {
    key = uuid()
  }

  provisioner "local-exec" {
    command = "num1=${var.minion-count} && num2=1 && num3=$[num1 - num2] && for i in $(seq 0 $num3); do echo IP_ADDRESS_HERE > /etc/ansible/machan; done"
  }
 }

```

The above would still not do what you intended of course, but Terraform should at least be able to parse it and run the `local-exec` provisioner and see the shell fail to process it.

With that said, I think there is a problem with your approach here: you are trying to iterate inside the shell but iterate over data that exists inside Terraform. The shell can’t see the data from Terraform and Terraform can’t see data from the shell, so this is not possible: your `i` variable exists only in the shell, and `aws_instance.kubernetes_minion` exists only inside Terraform.

It is possible in theory to have Terraform _generate_ a shell script that contains multiple IP addresses in a variable and then have the shell iterate over the contents of that variable, but [provisioners are a last resort](https://www.terraform.io/docs/provisioners/index.html#provisioners-are-a-last-resort) so we should first consider if there is a different way to solve this problem without provisioners at all.

It looks like your underlying goal is to generate a file `/etc/ansible/machan` which contains a list of IP addresses. Using Terraform to manage system-level files is quite unusual – Terraform is primarily for managing remote infrastructure objects, not local system configuration – but in unusual situations where you need to generate files from Terraform [the `local_file` resource type](https://www.terraform.io/docs/providers/local/r/file.html) allows you to declare that a file ought to exist using a normal Terraform resource, without any provisioners:

```auto
resource "local_file" "example" {
  content = join("\n", aws_instance.kubernetes_minion[*].public_ip)
  filename = "/etc/ansible/machan"
}

```

The above still has a problem: unless you are running Terraform as `root` (which would _also_ be very unusual) it will likely not be able to create a file in `/etc/ansible`. I might instead prefer to generate a file in the local working directory (like `"${path.cwd}/ansible-machan"`) and then copy that file into `/etc/ansible/machan` as a separate step once Terraform has succeeded, so that only that copy operation would be run with elevated permissions.

---

<div class="post-metadata">

**Author:** ![iamdempa](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/iamdempa/32/5491_2.png) [@iamdempa](https://discuss.hashicorp.com/u/iamdempa)\
**Post date:** [July 7, 2020, 6:06am UTC](https://discuss.hashicorp.com/t/how-to-interpolate-in-terraform/11020/3 "2020-07-07T06:06:28Z")

</div>

Wow! This answer is quite good. Thank you for your time. I will have to try this and get back to you. TY @apparentlymart

---

<div class="post-metadata">

**Author:** ![iamdempa](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/iamdempa/32/5491_2.png) [@iamdempa](https://discuss.hashicorp.com/u/iamdempa)\
**Post date:** [July 19, 2020, 5:09pm UTC](https://discuss.hashicorp.com/t/how-to-interpolate-in-terraform/11020/4 "2020-07-19T17:09:53Z")

</div>

@apparentlymart is it possible to add a `content` as follows?

```auto
[kube-master]
<some-ips>
[kube-minion]
<some-ips>

```

---

<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:** [July 20, 2020, 5:44pm UTC](https://discuss.hashicorp.com/t/how-to-interpolate-in-terraform/11020/5 "2020-07-20T17:44:33Z")

</div>

Hi @iamdempa,

That seems like a good use-case for [the `templatefile` function](https://www.terraform.io/docs/configuration/functions/templatefile.html), which use [Terraform’s string template syntax](https://www.terraform.io/docs/configuration/expressions.html#string-templates) with templates in external files.

For example, you could create a new file `roles.ini.tmpl` (or whatever you want) in the same directory as your module’s `.tf` files with the following content:

```auto
[kube-master]
%{ for addr in master_ip_addrs ~}
${addr}
%{ endfor ~}
[kube-minion]
%{ for addr in minion_ip_addrs ~}
${addr}
%{ endfor ~}

```

Then replace your `content` expression with a call to render that template:

```auto
  content = templatefile("${path.module}/roles.ini.tmpl", {
    master_ip_addrs = aws_instance.kubernetes_master[*].public_ip
    minion_ip_addrs = aws_instance.kubernetes_minion[*].public_ip
  })

```

---

<div class="post-metadata">

**Author:** ![iamdempa](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/iamdempa/32/5491_2.png) [@iamdempa](https://discuss.hashicorp.com/u/iamdempa)\
**Post date:** [July 30, 2020, 7:10pm UTC](https://discuss.hashicorp.com/t/how-to-interpolate-in-terraform/11020/6 "2020-07-30T19:10:40Z")

</div>

`Terraform` is getting really better…

So one question before I try this exciting feature. I don’t use any modules in my repo. So in the root directory I have `main.tf` and other `.tf` files. I don’t have a `module` directory. So my directory is like this:

```auto
/
  main.tf
  variable.tf
  terraform.tfvars
  src /
        Readme.md

```

So basically all `.tf` files are in the root directory and there is only one folder `src` which doesn’t contain anything related to the `terraform`

So how can I then give the path to my template if my template file is put in the `root` directory as below:

```auto
/
  main.tf
  variable.tf
  terraform.tfvars
  hosts.tmpl # new template
  src /
        Readme.md

```

So is this how this path should be?

```auto
templatefile("./roles.ini.tmpl", ...

```

Thank you for your time 🙂

---

<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:** [July 31, 2020, 10:58pm UTC](https://discuss.hashicorp.com/t/how-to-interpolate-in-terraform/11020/7 "2020-07-31T22:58:49Z")

</div>

Hi @iamdempa,

The root directory containing `.tf` files is, in most ways, just another module – we call it the “root module” – and so it still makes sense to use `path.module` in there to refer to files in the same directory as the `.tf` file containing the expression.

You can specify the path to that `hosts.tmpl` file you showed as `"${path.module}/hosts.tmpl"`. `path.module` will resolve just to `.` in this case, resulting in `./hosts.tmpl`.

---

<div class="post-metadata">

**Author:** ![iamdempa](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/iamdempa/32/5491_2.png) [@iamdempa](https://discuss.hashicorp.com/u/iamdempa)\
**Post date:** [August 1, 2020, 10:46am UTC](https://discuss.hashicorp.com/t/how-to-interpolate-in-terraform/11020/8 "2020-08-01T10:46:52Z")

</div>

Thank you @apparentlymart for this nice explanation. 🙂
