Prevent terraform function 'templatefile' from outputting heredoc

1k views Asked by At

I have a cloud-init config file to be used to spin up VMs in AWS using Terraform that I also want to run locally using Multipass for testing and debugging.

The cloud-init file includes some shell scripts which Terraform requires to be rewritten such that they refer to variables as $$var instead of just $var. It also uses a template variable for the hostname.

To be able to use the same config file on both cloud and locally, I figured that I could use the following command to expand the templating and start the Multipass VM without manual string replacements:

terraform console <<< "templatefile(\"cloud-init.yaml\", {hostname: \"test\"})" | multipass launch -n test --cloud-init -

But it turns out that the templatefile function wraps its output in heredoc:

<<EOT
... contents ...
EOT

Multipass of course doesn't understand this syntax and prints the (rather unhelpful) error message:

launch failed: operator[] call on a scalar (key: "users") 

Ideally, the Terraform command should be able to render the file without heredoc wrapper. But alternatively, is there a shell trick that would make this work?

2

There are 2 answers

0
bisgardo On

It's a little gross, but piping the contents through sed '1d;$d' removes the first and last lines which contain the heredoc markers:

terraform console <<< 'templatefile("cloud-init.yaml", {hostname: "test"})' | sed '1d;$d' | multipass launch -n test --cloud-init -

This is of course fragile as e.g. whitespace changes could break the assumptions of where the markers are. This solution should therefore be considered a hack/workaround in lack of a better one.

0
pcort On

Late to the party, but better late than never. My scenario was taking a k8s yaml file and using it as a template file for the kubernetes_manifest provider, and ran into the same problem of the heredoc formatting breaking the provider. Ended up using yamldecode() and it cleared out the heredoc formatting.

manifest = yamldecode(templatefile("./hpa.yaml", var.hpavalues))

Not the cleanest depending on the format you get the file in. in your case you'd probably have to do something like:

terraform console <<< 'yamlencode(yamldecode(templatefile("cloud-init.yaml", {hostname: "test"})))' | multipass launch -n test --cloud-init -

But there are a couple options, yamldecode(), jsondecode(), and their respective encode partners.