Looking for a way to render template directories including templated directory names

Is there a way to achieve similar functionality to the way cookiecutter works, where template tags can be used in directory/file names?
Alternatively- any way to force interpolating a string a second time could be a workaround?

Well, could you provide a real world example?

With cookiecutter, I can create the following directory tree from which to render another directory full of files, for example:
{{project_dir}}/
– file1. tmpl
– {{docs_dir}}/
---- readme.md.tmpl

Rendering that structure with values - project_dir = “foo”, docs_dir=“bar” would result in:
foo/
– file1
– bar/
---- readme.md

With hashicorp/dir/template, the filenames don’t get rendered, only the file contents.
I’m fairly sure the same is true with the template_dir provider but I really don’t want to create local files anyway.

I may have a hacky workaround which is implement the tag replacement manually, looping over the hashicorp/dir/template results and replace each tag with a value but it’s extremely hardcoded.

For example:

module "template_files" {
  source = "hashicorp/dir/template"
  base_dir = "${path.module}/templates"
  template_vars = {
    project_dir = "foo"
    docs_dir = "bar"
  }
}

locals {
  _iter1 = { for file, values in module.template_files.files : "${replace(file,"{{project_dir}}", "foo")}" => values }
# ... more iters for more variable replacements
  rendered_template_files = { for file, values in module.template_files.files : "${replace(file,"{{docs_dir}}", "bar")}" => values }
}

It would be great if the module would handle interpolation of the file names itself though i didn’t see a way to do that. Alternatively a templatestring() or interpolatestring() function would seem to solve the problem. We could name a directory something like "${project_dir}/ and then

locals {
  rendered_template_files = { for file, values in module.template_files.files : "${templatestring(file)}" => values }
}

I guess the main difference between templatestring and interpolatestring would be whether or not you had to pass a map of template vars or would you use the regular var.x syntax.

I’m not entirely sure, but have you considered using Jinja2 templates? They allow you to use template tags in file names and legal directories, which might achieve what you’re looking for. Another workaround could be using a script to interpolate the string twice. Hope this helps!

I guess I missed this the first time around, so I’ll belatedly reply now!

It’s true that the hashicorp/dir/template module was not designed to support that particular situation, and I don’t expect we’ll extend it further because we consider that module to be essentially “done”, bugs notwithstanding.

I think the main challenge with what you’re aiming to do here is that Terraform doesn’t support dynamic evaluation of templates from strings, since that would amount to a sort of “eval” function that would inhibit Terraform’s configuration analyses.

However, if you’re happy with just simple substitution of placeholders, rather than full template evaluation, then it would be possible in principle to generalize what you’re doing with replace to first tokenize the string using regexall and then use a for expression to replace the parts that are interpolation markers:

regexall("(?:\\{\\{\\w+}}|[^{]+|\\{)", input_string)

Given a string like {{project_dir}}/example the above would return:

tolist([
  "{{project_dir}}",
  "/example",
])

…so you can assume that any element which starts with {{ and ends with }} is a substitution sequence, and so look up the given name in a map to replace that sequence. Then you can join the list of strings back into a single string again (with no delimiters) to produce a result.

I probably wouldn’t go to the trouble of generalizing this just for one specific situation, but this extra tokenizing and transformation could be worth doing if it were going to be in a shared module similar to hashicorp/dir/template.