The templatestring function has some special constraints on its first
argument that are included to add some intentional friction for those who
are new to Terraform, want to do some simple template rendering, but have
only found the templatestring function so far.
We know from previous experience with the hashicorp/template provider that
this sort of functionality tends to attract those who haven't yet learned
that the Terraform language has built-in support for string templates
(without calling any function), who would then get confused by the need
for an extra level of escaping to render a template string only indirectly
through this function.
However, this rule is not intended to be onerous and require writing the
rest of the containing module in an unnatural way to work around it, so
here we loosen the rule to allow some additional forms:
- An index expression whose collection operand meets these rules.
- A relative traversal whose source operand meets these rules.
In particular this makes it possible to write an expression like:
data.example.example[each.key].result
...which is a relative traversal from an index from a scope traversal,
and is a very reasonable thing to write if you've retrieved multiple
templates using a data resource that uses for_each.
This also treats splat expressions in the same way as index expressions
at the static check stage, but that's only to allow us to reach the
dynamic type check that will ultimately report that a string is required,
because the result of a splat expression is a tuple. The type-related
error message is (subjectively) more helpful/relevant than the
syntax-related one for this case.
Finally, this includes some revisions to the documentation for this
function to correct some editing errors from the first pass and to slightly
loosen the language about what's allowed. It's still a little vague about
what exactly is allowed, but I'm doubtful that a precise definition in
terms of HCL's expression types would be very enlightening for a typical
reader anyway. We can tweak the specificity of the language here if we
start to see repeated questions about what is and is not valid.
This function complements the existing "templatefile" to deal with the
unusual situation of rendering a template that comes from somewhere
outside of the current module's source code, such as from a data resource
result.
We have some historical experience with the now-deprecated
hashicorp/template provider and its template_file data source, where we
found that new authors would find it via web search and assume it was
"the way" to render templates in Terraform, and then get frustrated
dealing with the confusing situation of writing a string template that
generates another string template for a second round of template rendering.
To try to support those who have this unusual need without creating another
attractive nuisance that would derail new authors, this function imposes
the artificial extra rule that its template argument may only be populated
using a single reference to a symbol defined elsewhere in the same module.
This is intended to entice folks trying to use this function for something
other than its intended purpose to refer to its documentation (once
written) and then hopefully learn what other Terraform language feature
they ought to have used instead.
The syntax restriction only goes one level deep, so particularly-determined
authors can still intentionally misuse this function by adding one level
of indirection, such as by building template source code in a local value
and then passing that local value as the template argument. The restriction
is in place only to reduce the chances of someone _misunderstanding_ the
purpose of this function; we don't intend to prevent someone from actively
deciding to misuse it, if they have a good reason to do so.
This new function inherits the same restriction as templatefile where it
does not allow recursively calling other template-rendering functions.
This is to dissuade from trying to use Terraform templates "at large",
since Terraform's template language is not designed for such uses. It would
be better to build a Terraform provider that wraps a more featureful
template system like Gonja if someone really does need advanced templating,
beyond Terraform's basic goals of being able to build small configuration
files, etc.
Because this function's intended purpose is rendering templates obtained
from elsewhere, this function also blocks calls to any of Terraform's
functions that would read from the filesystem of the computer where
Terraform is running. This is a small additional measure of isolation to
reduce the risk of an attacker somehow modifying a dynamically-fetched
template to inspire Terraform to write sensitive data from the host
computer into a location accessible to the same attacker, or similar.
This is currently only a language experiment and so will not yet be
available in stable releases of Terraform. Before stabilizing this and
committing to supporting it indefinitely we'll want to gather feedback on
whether this function actually meets the intended narrow set of use-cases
around dynamic template rendering.
If the string to be tested is an unknown value that's been refined with
a prefix and the prefix we're being asked to test is in turn a prefix of
that known prefix then we can return a known answer despite the inputs
not being fully known.
There are also some other similar deductions we can make about other
combinations of inputs.
This extra analysis could be useful in a custom condition check that
requires a string with a particular prefix, since it can allow the
condition to fail even on partially-unknown input, thereby giving earlier
feedback about a problem.
cty's new "refinements" concept allows us to reduce the range of unknown
values from our functions. This initial changeset focuses only on
declaring which functions are guaranteed to return a non-null result,
which is a helpful baseline refinement because it allows "== null" and
"!= null" tests to produce known results even when the given value is
otherwise unknown.
This commit also includes some updates to test results that are now
refined based on cty's own built-in refinement behaviors, just as a
result of us having updated cty in the previous commit.
This is part of a general effort to move all of Terraform's non-library
package surface under internal in order to reinforce that these are for
internal use within Terraform only.
If you were previously importing packages under this prefix into an
external codebase, you could pin to an earlier release tag as an interim
solution until you've make a plan to achieve the same functionality some
other way.