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.49k forks source link

A method for recursively rendering templates #35441

Open FireDrunk opened 2 months ago

FireDrunk commented 2 months ago

Terraform Version

Terraform v1.9.1
on linux_amd64

Terraform Configuration Files

main.tf

output "result" {
  value = templatefile("base.sh.tftpl", {
    my_var = "my_value"
  })
}

base.sh.tftpl

#!/bin/bash

echo "Base knows my_var is: ${my_var}"

${file("included.sh.tftpl")}

included.sh.tftpl

echo "Include knows my_var is: ${my_var}"

Debug Output

Changes to Outputs:
  + result = <<-EOT
        #!/bin/bash

        echo "Base knows my_var is: my_value"

        echo "Include knows my_var is: ${my_var}"
    EOT

Expected Behavior

I would expect templatefile to proplery render the output from file.

Actual Behavior

The file function renders the contents of the file, but for some reason, templatefile is not rending that content again. it seems that templatefile's logic is not run after the file logic, but before.

This might also have to do with the ${ } around file, which might triggers resolving nested variables in the 'local' scope, but not exactly in the templatefile's scope. So there might just be some scoping issues.

Steps to Reproduce

  1. Run terraform plan

Additional Context

No response

References

No response

jbardin commented 2 months ago

Hi @FireDrunk,

What you've shown here is working as designed, so I'm going to relabel this as an enhancement request. Templates are not recursive, so once the "${...}" string has been rendered there is no second pass to re-evaluate any new interpolation strings. If you were to attempt to re-write this using a nested templatestring function to accept the file() result, you would see an error like cannot recursively call templatestring from inside another template function.

FireDrunk commented 2 months ago

Ah, thanks for clarifying! I was under the impression that recursively calling templatefile was unsupported, but the use of file was. The linked functions at the bottom of this docs page might suggest that.

apparentlymart commented 2 months ago

To be clear, recursively using file is supported, but that function is defined as inserting the content of the file literally into the result, rather than interpreting it as a template, and that's why this is "working as designed".

Literally including a file that happens to contain the character sequences from Terraform's template language is essentially the same as passing a variable containing template syntax into the template and then interpolating it: in that case Terraform would similarly just insert the string literally, without evaluating its content as a template, because the template parsing happens before any expressions get evaluated, whether that be expressions that refer to variables or expressions that call functions.


Terraform is not designed to support large-scale "programming with templates" and its template language is not really sophisticated enough to permit writing and maintaining complex template libraries with lots of reuse and indirection.

For those that want to perform more complex templating, I expect it would be more ergonomic to write a Terraform provider that wraps a more advanced template engine and exposes it using provider-defined functions. For example, a provider that wrapped one of the several Go implementations of the Jinja2 template language could support features like macros and block instantiations that tend to make larger template libraries more maintainable and composable.

If somebody wrote such a provider then it would be usable with Terraform v1.8 and later, and it would be up to the designer of that provider what limits (if any) it would impose on various kinds of recursive evaluation.