Can I escape curly bracket symbol in terraform? - terraform

I'm using the terraform template_file data resource to create a file that should be written to the dynamically-created EC2 instance when I apply the stack. In other words, I want this file to be created in the home folder of the newly-created EC2 instance. However, this file contains curly bracket syntax ${}, which terraform is trying to interpolate. How can I escape these curly brackets?
As background, I'm using the cloud-config syntax to write these files.
Ex:
${username} should be written to the file, not interpolated in terraform.
Even when I use the double dollar sign $$, terraform still fails because it can't find the variable:
... failed to render : <template_file>:105,18-26: Unknown variable; There is no variable named "username".

Terraform uses a fairly unique escape for curly braces:
Sequence
Replacement
$${
Literal ${, without beginning an interpolation sequence.
%%{
Literal %{, without beginning a template directive sequence.
Documentation for reference: https://www.terraform.io/docs/language/expressions/strings.html

FYI I ended up working around this by writing the template in another file, then reading it into the terraform stack using the file method:
data "template_file" "config" {
template = "${file("./user_data.tpl")}"
}

For example if you would like to add into Tags {{_payload.ref}} as it was in my case, I have done it this way:
tags = ["CRITICAL", "\u007B\u007B_payload.ref\u007D\u007D"]
so
\uNNNN Unicode character from the basic multilingual plane (NNNN is four hex digits)
ref.page: https://www.terraform.io/language/expressions/strings

Related

how to remove double quotes of a variable using trim in terraform

locals {
check_list ="test"
trimoutput = trim(local.check_list, "")
}
currently, the trim output value is still "test"
Expected output is just test. I need this value inside terraform code itself
if you want to include a literal quote mark then you must use a backslash key
locals {
check_list ="test"
trimoutput = trim(local.check_list, "\"")
I think you are confusing the value of the variable in memory with the syntax Terraform uses to show values in the CLI output.
If your goal is to use the raw value of a root module output value to pass on to some subsequent process then you can use the terraform output command with its -raw option to tell Terraform to output the value literally, rather than rendering it using normal Terraform language syntax (which for strings includes quotes).
terraform output -raw name_of_output_value
Note that there are no quotes in the string itself. The quotes are just the markers Terraform uses to understand that the characters within are intended to be a string.

Terraform Import strips quotations from index

I'm trying to import state from Azure via Terraform and save the state to a resource with indexes, such as:
terraform import azurerm_subnet.test_subnets[\"test-subnet-1\"] /subscriptions/xxxxxx-xxx-xxx-xxxxx/resourceGroups/test-rg/providers/Microsoft.Network/virtualNetworks/test-vnet/subnets/test-subnet-1
I read other threads that say I have to escape the quotation marks, so I did. Even though the quotes are escaped, it seems as though the Terraform compiler is still pulling the quotations out and leaving the \ characters in place.
Here's the error output that I'm seeing:
Error: Invalid character
on <import-address> line 1:
1: azurerm_subnet.test_subnets[\test-subnet-1\]
This character is not used within the language.
Error: Index value required
on <import-address> line 1:
1: azurerm_subnet.test_subnets[\test-subnet-1\]
Index brackets must contain either a literal number or a literal string.
Error: Invalid character
on <import-address> line 1:
1: azurerm_subnet.test_subnets[\test-subnet-1\]
This character is not used within the language.
For information on valid syntax, see:
https://www.terraform.io/docs/internals/resource-addressing.html
Any thoughts here? Am I doing something wrong or is this a known limitation of the application?
In PowerShell, the import command should be like this:
terraform import 'azurerm_subnet.test_subnets[\"test-subnet-1\"]' subnet_resource_id
The screenshot here shows the output:
But if you use the cmd in windows, the command you use is also right. See more details about command import.

Invalid Template Control Keyword

Currently developing a TF template inclusive of some glue services. When attempting to init the template, I am prompted with the below error:
Error: Invalid template control keyword
on main.tf, in resource "aws_glue_classifier" "SrcPlanClassifier":
grok_pattern = "%{DATA:col},%{DATA:col2},%{DATA:col3},"%{DATA:col4}",% .
{DATA:col5},%{DATA:col6},%{DATA:col7},%{DATA:col8},%{DATA:col9},%{DATA:col10},% .
{DATA:col11},%{DATA:col12},%{DATA:col}13$"
"DATA" is not a valid template control keyword.
This template is the result of translating an existing CloudFormation template to Terraform, so the pattern has worked in the past. From what I can tell in the AWS documentation for Glue, the DATA term is an acceptable built-in classifier. That implies to me that this is an issue on the Terraform end. What am I missing here?
Thanks in advance for the help!
Terraform is understanding the %{ sequences as introducing a template directive, which then fails because if and for are the only keywords that are allowed to follow that %{ marker in the Terraform language.
To use %{ literally in your string, you can write %%{ instead to escape the introducer. Terraform will see %%{ and produce %{ in the resulting string:
grok_pattern = "%%{DATA:col},%%{DATA:col2},%%{DATA:col3},"%%{DATA:col4}",% .
{DATA:col5},%%{DATA:col6},%%{DATA:col7},%%{DATA:col8},%%{DATA:col9},%%{DATA:col10},% .
{DATA:col11},%%{DATA:col12},%%{DATA:col}13$"
(I think there were some line-wrapping problems in the message you shared so I've updated it as best I could for the escaping but you may need to do some additional escaping yourself. The general idea is to replace every %{ with %%{ in your quoted string.)
Another option, for complicated expressions whose readability is hurt significantly by this much escaping, is to move the relevant string into a separate file and then have Terraform read that file:
grok_pattern = file("${path.module}/grok_pattern.txt")
The file function just takes the text in the given file verbatim, and does not parse it for template sequences or any other special markers.

How to interpolate expressions in Terraform?

I'm trying to use the keys expression in Terraform to grab a list of keys (from a map variable) and assign it to a local variable. Here is the code snippet:
locals {
project_name_list = keys(${var.project_map})
}
However, I'm getting the following error:
Unknown token: 29:22 IDENT keys
Am I missing something here. Nowhere can I find an example of this expression. As bad as it is, even the official documentation does not help -https://www.terraform.io/docs/configuration/functions/keys.html
HashiCorp has really done a bad job of elaborating the nuances of Terraform for beginners on their website.
Terraform functions need to be wrapped in expression syntax to show that it's not a literal value: "${}"
So try this: project_name_list = "${keys(var.project_map)}"
The example in the documentation is written as though being run from the terraform command line, which already assumes the command is a HCL expression and doesn't require that syntax.
UPDATE
I said above that the expression syntax is to show that it's not a literal value. It's probably more accurate to speak of it as expression syntax vs. configuration syntax. Configuration syntax is the first level of interpolation, which forms the basic structure of your terraform file with resource blocks, data blocks, etc. The second interpolation level is expression syntax which is used to generate values used by your configuration.
Thinking of it in these terms makes better sense of the error message, Unknown token, because terraform is attempting to read it as a configuration key word.
I had compared it to a literal value because it's in the same position as where a literal value would be.

Can I use double quotes in Chef attribute declaration?

I have inherited a cookbook that sets some attributes in the ./attributes/default.rb file as per normal.
However, we have a problem with one of the lines is, which is:
default["obscured"]["mysql"] = "#{node['jboss']['jboss_home']}/modules/com/mysql/jdbc/main"
When run, it write this into the node as:
{}/com/mysql/jdbc/main
I can confirm that the node['jboss']['jboss_home'] attribute exists and has correct values.
So, I cannot see any problem with the above, except that every other declaration of this type in our cookbooks has single quotes on the attribute to be set (i.e. left side), not double quotes. I haven't heard this of as being an issue before, but I am pretty new to chef.
Is there any rule that says they must be single quotes?
The answer is that there is no rule.
Using double-quotes in something like this is completely fine:
default["obscured"]["mysql"] = blah blah
The reason I know that is that I just found one being set, with double quotes, that actually works. :-)
What you have there is fine, how are you confirming the value of node['jboss']['jboss_home'] and how are you using it in the template?
In Ruby single and double quoted literals both become Strings but single quotes are relatively literal while double quotes allow backslash escapes and #{} interpolation.
You are most likely hitting the derived attributes problem:
https://coderanger.net/derived-attributes/
The attribute code in your cookbook is getting parsed before the jboss_home attribute is being set. One way or another the solution is to move the interpolation into recipe code.
You could just use a plain old ruby variable instead of the attribute you are trying to construct -- particularly if nothing else in your system ever sets that attribute.
You also should be able to delete the declaration from your attributes file and use this in recipe code as well:
node.default_unless["obscured"]["mysql"] =
"#{node['jboss']['jboss_home']}/modules/com/mysql/jdbc/main"
Although you need to place that statement early in your run_list, before you ever use node["obscured"]["mysql"] as an argument to any resource.

Resources