How to have a literal string of ${something} in a template data file - terraform

I have a template file, that is creating a fluentd file and inserting various variables. I am now trying to include this plugin which expects to find its own variables in the config file. The problem is that Terraform defines a variable in a template like ${variable} and this plugin expects to find its variables in the file as literal ${variable}
How can I tell terraform to not interpolate a ${} in a file, but to actually pass that entire string?
File Snippet:
<filter tomcat.logs>
#type record_transformer
<record>
customer ${customer}
environment ${environment}
application ${application}
</record>
</filter>
The above ${} are all variables I have defined for my template. I now need to add a section like this.
<record>
hostname ${tagset_name}
instance_id ${instance_id}
instance_type ${instance_type}
az ${availability_zone}
private_ip ${private_ip}
vpc_id ${vpc_id}
ami_id ${image_id}
account_id ${account_id}
</record>
Where all of those are not variables but how it actually needs to look in the rendered template. I tried swapping them to be like $${account_id}, but that just ends up rendering account_id in the file.
data "template_file" "app" {
template = "${file("templates/${var.application}.tpl")}"
vars {
customer = "${var.customer}"
environment = "${var.environment}"
application = "${var.application}"
}
}
Here is a breakdown of what is happening.
In the user data I have "instance_type $${instance_type}"
The launch configuration that is created for the instances, shows "instance_type ${instance_type}"
The actual file that is present on AWS shows "instance_type"

Finally figured this out. The answer from the marked duplicate question is incorrect for this instance.
template.tpl contains
cat <<EOT > /root/test.file
db.type=${db_type}
instance_type \$${instance_type}
EOT
Result
Error: Error refreshing state: 1 error(s) occurred:
* module.autoscaling_connect.data.template_file.app: 1 error(s) occurred:
* module.autoscaling_connect.data.template_file.app: data.template_file.app: failed to render : 27:16: unknown variable accessed: bogus_value
template.tpl contains
cat <<EOT > /root/test.file
db.type=${db_type}
instance_type \$${instance_type}
EOT
Results in a launch configuration containing
cat <<EOT > /root/test.file
db.type=mysql
instance_type \${instance_type}
EOT
Results in the File we created on the instance containing
db.type=mysql
instance_type ${instance_type}
In Short to end up with a ${something} in the file created from a terraform template file, you have to use \$${something} in the .tpl file.

In my case, having this issue inside a resource, the value is inside quotes and I don't need the \ to fix it.
somevalue = "$${variable}"
produces "${variable}" instead of "my_value"

Related

Terraform . How to pass multiple values in command line using list (string) in variable.tf file?

I have a simple main and variable files for deploying webapp for containers in Azure.
But I would like that terraform plan uses variables from the command line to choose names like follows:
terraform plan -var resource_group_name=my-rg
This worked perfectly commenting the name of the default value for the RG like this.
main.tf
data "azurerm_resource_group" "my-rg" {
name = var.resource_group_name
}
variable.tf
variable "resource_group_name" {
# default = "Search-API"
}
But if I want to do the same for a list string I don´t know how to do it. I want to be able to do something that If I put 2 names 2 webapps are going to be created, if I put 3, 3 webapps and so.
I tried with this (also commenting default value) :
main.tf
resource "azurerm_app_service" "azure-webapp" {
count = length(var.webapp_server_name)
name = var.webapp_server_name[count.index]
variable.tf
variable "webapp_server_name" {
description = "Create Webapp with following names"
type = list(string)
#default = ["webapp-a", "webapp-b", "webapp-c"]
But I´m getting:
terraform plan -var webapp_server_name=webapp-a
Error: Variables not allowed
on <value for var.webapp_server_name> line 1:
(source code not available)
Variables may not be used here.
I also tried with empty string like:
variable "webapp_server_name" {
description = "Create Webapp with following names"
type = list(string)
default = []
}
Is there a way to do such a thing with terraform? to define an empty list and pass values (one, two, or more) from command?
thanks
UPDATE
Tried like this, following this post but now is asking to put the value even though I´m passing it through command line
terraform plan -var 'listvar=["webapp-a"]'
var.webapp_server_name
Create Webapp with following names
Enter a value:
If there is a variable declaration:
variable "webapp_server_name" {
description = "Create Webapp with following names"
type = list(string)
#default = ["webapp-a", "webapp-b", "webapp-c"]
}
You could use it like this with \ to escape the quotes".
terraform plan -var 'webapp_server_name=[\"webapp-a\", \"webapp-b\", \"webapp-c\"]'
For example, it worked with using the latest terraform provider version Terraform v0.13.4.
When you pass in the variable from the command line with -var webapp_server_name=webapp-a you are passing it in as a string.
But you've defined the variable as a list. So based on the docs you'll want the command line to look something like:
terraform plan -var='webapp_server_name=["webapp-a"]'

terraform; purpose of redundant "name" key?

Looking at the definition of a Resource here: https://www.terraform.io/docs/configuration/resources.html, you see examples like this:
resource "aws_iam_role" "example" {
name = "example"
# assume_role_policy is omitted for brevity in this example. See the
# documentation for aws_iam_role for a complete example.
assume_role_policy = "..."
}
The first example in the top line is called the "name" and can be used to reference, eg aws_iam_role.example. However, there is no discussion of 1) why there is also a key called "name" and why it is redundantly set to the same string "example". What is the purpose of this inner key name = example, and what is the best practices here?
EDIT: this seems related: https://github.com/hashicorp/terraform/issues/16394
"aws_iam_role" "example" - you reference this resource in your terraform config (e.g. aws_iam_role.example, must be unique at least within one module)
name = "example" - is the name of your resource on the cloud side (must be unique on the cloud side, e.g. within one project or region, depends on the provider)
At the same time they could be different in your terraform config, for example:
resource "aws_iam_role" "my_role" {
name = "${terraform.workspace}-example-role"
# assume_role_policy is omitted for brevity in this example. See the
# documentation for aws_iam_role for a complete example.
assume_role_policy = "..."
}
PS: in this case (with aws_iam_role resource) the name argument is optional. If you skip it, you will see a randomly generated resource name in your AWS console.

Combine Variable Values and Explicitly Defined Variable Values in Terraform Tags for AWS

Currently, I'm working on a requirement to make Terraform Tags for AWS resources more modular. In this instance, there will be one tag 'Function' that will be unique to each resource and the rest of the tags to be attached will apply to all resources. What I'm trying to do is combine the unique 'Function' value with the other tags for each resource.
Here's what I've got so far:
tags = {
Resource = "Example",
"${var.tags}
This tags value is defined as a map in the variables.tf file like so:
variable "tags" {
type = map
description = "Tags for infrastructure resources."
}
and populated in the tfvars file with:
tags = {
"Product" = "Name",
"Application" = "App",
"Owner" = "Email"
}
When I run TF Plan, however, I'm getting an error:
Expected an attribute value, introduced by an equals sign ("=").
How can variables be combined like this in Terraform? Thanks in advance for your help.
Figured this one out after further testing. Here you go:
tags = "${merge(var.tags,
map("Product", "Product Name",
"App", "${var.environment}")
)
}"
So, to reiterate: this code will merge a map variable of tags that (in my case) are applicable to many resources with the tag (Product and App) that are unique to each infrastructure resource. Hope this helps someone in the future. Happy Terraforming.
I tried to use map, it does work with new versions.
The lines below works for me:
tags = "${merge(var.resource_tags, {a="bb"})}"
Creating values in my tfvars file did not work for me...
Here is my approach....
I created a separate variable in my variables.tf file to call during the tagging process..
my default variable for tags are imported/pass from a parent module.
So therefore it doesnt need to specify any default data.
the extra tagging in the child module is done in the sub_tags variable..
imported/passed from parent/root module
variable "tags" {
type = "map"
}
tags in the child module
variable "sub_tags"{
type = "map"
default = {
Extra_Tags_key = "extra tagging value"
}
}
in the resource that needs the extra tagging.. i call it like this
tags = "${merge(var.tags, var.sub_tags)}"
this worked great for me

Terraform interpolation syntax error for data template_file

In my lambda.tf, I have a data resource
data "template_file" "handler" {
template = "${file("${path.module}/templates/handler.js")}"
vars = {
ENDPOINT = "${var.domain}"
PASSWORD = "${var.password}"
}
}
However - I'm encountering a syntax error:
Error: failed to render : <template_file>:280,49-50: Extra characters after interpolation expression; Expected a closing brace to end the interpolation expression, but found extra characters.
on ../docs/lambda.tf line 1, in data "template_file" "handler":
1: data "template_file" "handler" {
Is interpolation inside an interpolation allowed for Terraform? If so - any suggestions on pointing towards where the error is would be greatly appreciated.
Terraform v0.12.9.
Provider "aws" version "~> 2.7"
Not exactly clear what your template file looks like or what you are trying to do, so here are a couple different answers.
You can escape interpolation with double dollar signs: $${foo} will be rendered as a literal ${foo}.
Terraform does not allow dynamic construction of variable names, because it needs to be able to analyze the configuration statically (that is, without evaluating any expressions) in order to determine which order the expressions must be resolved in.
Terraform supports a map data structure that can be used to achieve this effect.
variable "var1" {
default = "value1"
}
variable "var2" {
default = "value2"
}
locals {
var3 = "${var.var1}_${var.var2}"
values = {
"value1_value2" = "local1"
"value2_value3" = "local2"
"value3_value4" = "local3"
}
}
output "val_output" {
value = "${local.values[local.var3]}"
}
If neither is what you are looking for, you need to share your template file or a modified version that duplicates the error.
The template_file data source continues to exist for users of Terraform 0.11 and earlier, but since you are using a Terraform 0.12 release I'd recommend using the templatefile function instead. Because it's built directly into Terraform, it is able to produce better error messages.
To use it, you can replace your references to data.template_file.handler.rendered with a direct call to templatefile. If you are using that rendered result in multiple locations, you can assign the templatefile result to a local value and reference that in multiple places instead.
templatefile("${path.module}/templates/handler.js", {
ENDPOINT = var.domain
PASSWORD = var.password
})
The error message you saw suggests that there's a syntax error in your template itself, but because template_file is implemented in a separate provider it's reporting that syntax error in an unhelpful way, pointing to a particular source location but not including the relevant source code snippet.
If you use templatefile instead, Terraform can hopefully report this syntax error itself and give better information about it.
Either way, it seems like the syntax error is on line 280 column 49 of your handler.js file and is caused by Terraform's template engine expecting to find the } to close a ${ ... } interpolation sequence but finding something else instead. If you correct that syntax error, template rendering should succeed by either approach.

Terraform variable files

I am trying to use a variables file to deploy resource groups in Azure using Terraform but it works if I only have one variable. If I use two I get an error:
"invalid value "variables.tf" for flag -var-file: multiple map
declarations not supported for variables"
The variables file is as below :
variable "resource_group_name" {
description = "The name of the resource group in which the resources will be created"
default = "im-from-the-variables-file"
}
variable "location" {
description = "The location/region where the virtual network is created. Changing this forces a new resource to be created."
default = "west europe"
}
The main file used to deploy is as below:
resource "azurerm_resource_group" "vm" {
name = "${var.resource_group_name}"
location = "${var.location}"
}
You've confused the variable definition syntax to the variable setting syntax.
Terraform will concatenate all the .tf files in a directory so your variables.tf file (assuming it's in the same directory as your main.tf (or whatever contains your azurerm_resource_group resources etc) is already included.
You need to define every variable before it can be used so things like:
resource "azurerm_resource_group" "vm" {
name = "${var.resource_group_name}"
location = "${var.location}"
}
by themselves would not be valid as the variables resource_group_name and location are not defined.
You define variables with the syntax you've used in your variables.tf file:
variable "location" {
description = "The location/region where the virtual network is created. Changing this forces a new resource to be created."
default = "west europe"
}
To override the defaults (if wanted or if a default isn't provided) then you need to either pass the variable in at run time (using TF_VAR_location environment variables or by using -var location="west us") or you can define vars files that take the form:
location = "west us"
resource_group_name = "im-from-the-variables-file"
Terraform will automatically load any files in the directory called terraform.tfvars or *.auto.tfvars and you can also define extra vars files at any point by using -var-file=myvars.tfvars as you have attempted to do (but with a .tf file containing HCL instead of key-pairs.
Adding to what ydaetskcoR has mentioned in above answer. If you have already specified default values in variable file for all variables defined and you need just that default values you don't even need to pass -var-file since default values will be used if you don't pass values

Resources