Terraform > Unescaped interpolations - terraform

What does this mean:
Note: Inline templates must escape their interpolations (as seen by the double
$ above). Unescaped interpolations will be processed before the template.
from https://www.terraform.io/docs/providers/template/index.html
The specific example is:
# Template for initial configuration bash script
data "template_file" "init" {
template = "$${consul_address}:1234"
vars {
consul_address = "${aws_instance.consul.private_ip}"
}
}

The ${} syntax is used by HCL for interpolation before the template rendering happens so if you were to just use:
# Template for initial configuration bash script
data "template_file" "init" {
template = "${consul_address}:1234"
vars {
consul_address = "${aws_instance.consul.private_ip}"
}
}
Terraform will attempt to find consul_address to template into the output instead of using the template variable of consul_address (which in turn is resolved to the private_ip output of the aws_instance.consul resource.
This is only an issue for inline templates and you don't need to do this for file based templates. For example this would be fine:
int.tpl
#!/bin/bash
echo ${consul_address}
template.tf
# Template for initial configuration bash script
data "template_file" "init" {
template = "${file("init.tpl")}"
vars {
consul_address = "${aws_instance.consul.private_ip}"
}
}
Of course if you then also needed to use the ${} syntax literally in your output template then you would need to double escape with something like this:
#!/bin/bash
CONSUL_ADDRESS_VAR=${consul_address}
echo $${CONSUL_ADDRESS_VAR}
This would then be rendered as:
#!/bin/bash
CONSUL_ADDRESS_VAR=1.2.3.4
echo ${CONSUL_ADDRESS_VAR}

Related

include a terraform template in another template

I have many template files that are used by terraform scripts, all template files have some common part, ie:
file a.tmpl:
env=prod
var=a
-------------------
file b.tmpl:
env=prod
var=b
I would like to export the common part to a separate file, so that it won't have to repeat in every file, something like:
file base.tmpl:
env=prod
-------------------
file a.tmpl:
%{ include "base.tmpl" }
var=a
-------------------
file b.tmpl:
%{ include "base.tmpl" }
var=b
but that feature doesn't exists
(it is very similar to django templates feature as described here: https://stackoverflow.com/a/10985987/245024)
is there a way to do the include somehow?
I was able to do a workaround by concating the files like this:
data "template_file" "vars_a" {
template = "${format("%s \n %s",
file("${path.module}/base.tmpl"),
file("${path.module}/a.tmpl")
)}"
}
but that is more limiting then including the base template directly in the file.
I think you could use templatefile:
a.tmpl
${file("base.tmpl")}
var=a
base.tmpl
var_ddd=ffff
var_sss=adfs
main.tf
data "template_file" "vars_a" {
template = templatefile("a.tmpl", {})
}
output "test" {
value = data.template_file.vars_a.template
}

There is no variable named "REGION"

The ec2 launch tempate userdata definition using the template provider is about to be replaced with a templatefile function as it is deprecated.
The definition itself can be done without any problems, but an error occurs during the planning process saying that the processing variables for getting instance metadata etc. are undefined, is there any way to avoid this?
$ terraform version
Terraform v0.13.5
data "template_file" "userdata" {
template = templatefile("${path.module}/userdata.sh.tpl",
vars....
)
}
resource "aws_launch_template" "sample" {
....
user_data = base64encode(data.template_file.userdata.rendered)
....
}
The following definitions are applicable processing
export LOCAL_IP=$(ec2metadata --local-ipv4)
export GLOBAL_IP=$(ec2metadata --public-ipv4)
export REGION=$(curl -s http://169.254.169.254/latest/meta-data/placement/availability-zone | sed 's/[a-z]$//')
export AWS_DEFAULT_REGION="$${REGION}"
$ terraform plan
Error: failed to render : <template_file>:13,30-36: Unknown variable; There is no variable named "REGION"., and 4 other diagnostic(s)
thanks
It doesn't make sense to use the template_file data source and the templatefile function together, because these both do the same thing: render a template.
In your current configuration, you have the templatefile function rendering the template to produce a string, and then the template_file function interpreting that result as if it were a template, and so your template is being evaluated twice.
The first evaluation will turn $${REGION} into ${REGION} as expected, and then the second evaluation will try to handle ${REGION} as a template interpolation.
Instead, remove the template_file data source altogether (it's deprecated) and just use the templatefile function:
user_data = templatefile("${path.module}/userdata.sh.tpl, {
# ...
})
(I removed the base64encode function here because user_data expects an unencoded string value where the provider itself will do the base64 encoding. If you call base64encode yourself here then you'll end up with two levels of base64 encoding, which I imagine is not what you intended.)

Terraform empty and non-empty block map variables

I want to make backend-service by using Terraform. I use resource_type google_compute_backend_service
Now, i have 2 backend-services created by gcloud command. The one is using cdn_policy block and another one doesn't use cdn_policy.
The first backend-services tfstate is like
...
"cdn_policy": [
{
"cache_key_policy": [],
"signed_url_cache_max_age_sec": 3600
}
]
...
And the second backend-services is like
"cdn_policy": []
How to create the terraform script works for both of them ? So, terraform script can run for backend-services who has cdn_policy include with its block map and can also run for backend-services without cdn_policy.
In my idea, i can create 2 terraform scripts. First for cdn_policy and second without cdn_policy. But, i think this is not best practice.
If i put cdn_policy = [], it would result error An argument named "cdn_policy" is not expected here
You can use dynamic blocks to create a set of blocks based on a list of objects in an input variable: Dynamic Blocks
resource "google_compute_backend_service" "service" {
...
dynamic "cdn_policy" {
for_each = var.cdn_policy
content {
cache_key_policy = cdn_policy.value.cache_key_policy
signed_url_cache_max_age_sec = cdn_policy.value.signed_url_cache_max_age_sec
}
}
}

Jenkins Parameterised Job with Terraform map variable

I'm trying to feed user-defined parameters from Jenkins into a job which runs terraform. I have a set of simple key/value vars, which I'm passing into the Terraform like this:
sh "terraform plan -var 'var1=${params.Var1}' -var 'var2=${params.Var2}' ...."
However, I also have a map var:
tags = {
Cluster = "mycluster"
Environment = "myenv"
Owner = "owner"
Product = "myproduct"
}
and I would like to feed some String Parameters into this map var. It looks like I need to define a groovy map and then render the map as JSON, so that I can have e.g. terraform -var 'tags={"Cluster":"mycluster","Environment":"dev"...}
My job is currently of the form:
pipeline {
agent {
...
}
options {
...
}
environment {
...
}
stages {
stage('MyStage') {
steps {
script {
sh "terraform ..."
}
}
}
}
}
I'me very new to Jenkinsfiles, so where would I define a map, if that's what was needed..?
Your attempt in the question is close, but Maps in Groovy are constructed with [:]. Therefore, you can construct a Jenkins Pipeline Map type output in JSON to be passed to a Terraform Map type like the following:
terraform -var "tags=${JsonOutput.toJson(['Cluster':'mycluster', 'Environment':'dev'...])}"
Given your use of the sh step method above, you will also need to escape your quotes to properly cast your strings appropriately as well.

Adding a long content using End of text string(EOT) in terraform is throwing error

I want to add a content to the file using terraform using a local provider. Here is the example script which I am using
terraform {
required_version = "~>0.13"
required_providers {
local = "~>1.4"
}
}
resource "local_file" "literature" {
filename = "art_of_war.txt"
content = <<EOT
Hello
world
EOT
}
I am getting the following error Expected the start of an expression, but found an invalid expression token. .Can you please point what might be error.
It seems that in your example you were using tabs instead of spaces (or you have it configured in your editor). I recreated your example with using only spaces and it worked. Here's the code snippet that works:
resource "local_file" "literature" {
filename = "art_of_war.txt"
content = <<EOT
Hello
World
EOT
}
Note that EOT is left-aligned to the same level as resource.
EDIT: Actually, it seems there's a whitespace after the <<EOT, if you delete it it should work.

Resources