Terraform: template_file: template attrib accepts interpolated string, but will not render vars

Created on 18 Nov 2017  ยท  5Comments  ยท  Source: hashicorp/terraform

Terraform Version

0.9.6 to Latest (0.11.0)

Terraform Configuration Files

variable "my_template" {
  default = "some_key: $${insert_here}"
}

data "template_file" "document" {
  template = "${var.my_template}"

  vars {
    insert_here = "some_value"
  }
}

output "info" {
  value = "${data.template_file.document.rendered}"
}

Debug Output

https://gist.github.com/dayglojesus/96375c2daa6ecedc398b729b2a585bca

Expected Behavior

The properly escaped $${insert_here} should be evaluated inside the template_file resource as a template variable (${insert_here}), not rendered as a literal string.

Actual Behavior

Template is rendered successfully, but the vars in the template are not substituted. Instead, the escaped vars ($$insert_here) are interpreted literally.

data.template_file.document:
  id = ed48139b5a707a192bc659d2d7303b35a5a74635029b80e3a0ef800e293cd00d
  rendered = some_key: ${insert_here}
  template = some_key: $${insert_here}
  vars.% = 1
  vars.insert_here = some_value

Outputs:

info = some_key: ${insert_here}

Steps to Reproduce

  1. terraform init
  2. terraform apply
bug config

Most helpful comment

Hi @dayglojesus! Sorry for this confusing behavior.

I suspect that this is behaving strangely because interpolation expressions are not expected in default, and therefore the usual pass to deal with them (which would also deal with the $${ escape sequence) doesn't apply here.

Our new configuration language parser, which we're working on integrating now, should address this by applying consistent processing to all strings regardless of context. In the mean time, I think this could be worked around by applying the default in a context where interpolation _is_ allowed, such as in a local value:

variable "my_template" {
  default = ""
}

locals {
  default_template = "some_key: $${insert_here}"
  template = "${var.my_template != "" ? var.my_template : local.default_template}"
}

data "template_file" "document" {
  template = "${local.template}"

  vars {
    insert_here = "some_value"
  }
}

output "info" {
  value = "${data.template_file.document.rendered}"
}

Since interpolation _is_ allowed in the default_template local value, it should be processed as expected and have that $${ escape transformed to ${ as you wanted here.

The improved configuration language parser is a pretty disruptive change, and so we're going to roll out it quite cautiously over the course of a few releases -- opt-in only at first, I expect -- but it should address this problem with no additional effort just as a side-effect of the fact that the new parser integrates both the structural parsing (the blocks and attributes) and the expression parsing (what's inside the ${ ... } sequences) into a single parser, which will avoid strange edge-cases where certain attribute values are parsed differently than others.

All 5 comments

Little more info on why this seems odd, even if it turns out to be _expected_ behaviour...

When the example code above is composed into a module and declared, passing the template as a string:

module "not_expected" {
  source      = "./modules/foo"
  my_template = "some_key: $${insert_here}"
}

It returns the results one might expect:

module.not_expected.data.template_file.document:
  id = e8b4f5517b665a628a7cdc4557d74c97ffab91d4ccd53b25dfc8140c52fb5839
  rendered = some_key: some_value
  template = some_key: ${insert_here}
  vars.% = 1
  vars.insert_here = some_value

Given this, it may have something to do with the order in which the the interpolation occurs, but it's maybe not an intuitive behaviour.

Hi @dayglojesus! Sorry for this confusing behavior.

I suspect that this is behaving strangely because interpolation expressions are not expected in default, and therefore the usual pass to deal with them (which would also deal with the $${ escape sequence) doesn't apply here.

Our new configuration language parser, which we're working on integrating now, should address this by applying consistent processing to all strings regardless of context. In the mean time, I think this could be worked around by applying the default in a context where interpolation _is_ allowed, such as in a local value:

variable "my_template" {
  default = ""
}

locals {
  default_template = "some_key: $${insert_here}"
  template = "${var.my_template != "" ? var.my_template : local.default_template}"
}

data "template_file" "document" {
  template = "${local.template}"

  vars {
    insert_here = "some_value"
  }
}

output "info" {
  value = "${data.template_file.document.rendered}"
}

Since interpolation _is_ allowed in the default_template local value, it should be processed as expected and have that $${ escape transformed to ${ as you wanted here.

The improved configuration language parser is a pretty disruptive change, and so we're going to roll out it quite cautiously over the course of a few releases -- opt-in only at first, I expect -- but it should address this problem with no additional effort just as a side-effect of the fact that the new parser integrates both the structural parsing (the blocks and attributes) and the expression parsing (what's inside the ${ ... } sequences) into a single parser, which will avoid strange edge-cases where certain attribute values are parsed differently than others.

This is a pretty easy workaround. Thanks @apparentlymart -- much obliged.

Hi again!

The configuration language improvements that @apparentlymart mentioned have now been released in Terraform 0.12. The change for variable defaults is that they are now parsed the same way as any other expression in the language, but references to other objects and function calls are both prohibited.

That means that interpolation sequences are themselves valid and must be escaped if a literal ${ is desired:

variable "my_template" {
  default = "some_key: $${insert_here}"
}

Although this is unlikely to be useful most normal cases, you can now use interpolation sequences and template control sequences in default as long as they only work with constant values, and don't refer to any objects or functions:

variable "my_template" {
  default = "This is rather ${"pointless"}, but it now works for consistency"
}

We wouldn't recommend doing things like that without a strong reason, but it became possible just as a result of parsing default the same way as any other expression in the language.

I'm going to lock this issue because it has been closed for _30 days_ โณ. This helps our maintainers find and focus on the active issues.

If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.

Was this page helpful?
0 / 5 - 0 ratings