how to concatenate a variable and a string in terraform? - terraform

I'm new to terraform and trying to add a variable to a string,
Suppose, id = "abcde", host =~ ${id} + "id", should return abcdeid
what's the best way to achieve this in terraform?

You can concatenate them directly or using join. For example:
variable "id" {
default = "abcde"
}
output "output1" {
value = "${var.id}id"
}
output "output2" {
value = join("", [var.id, "id"])
}
which will give:
output1 = abcdeid
output2 = abcdeid

Assuming a var called name and a suffix of -123
Host = “${var.name}-123”

Related

Terraform reduce the amount of loops at the moment of generating a JSON

I have this terraform code that is generating me this JSON.
{
host = {
path = "/xxxx/yyyy"
}
name = "NAME"
}
Currently it's working, but I have 3 loops, consider it not efficient, wondering if I can reduce it to 2 or probably 1 loop? Or this isn't possible.
My first loop validates that container_mounts isn't empty. Don't want to generate it, if that comes empty. The second and the third is specific for getting the information as container_mounts is a map of strings.
variable "container_mounts" {
type = map(string)
default = { "app/data" = "/xxxx/yyyyy" }
}
json = jsonencode(
[
for i in range(length(var.container_mounts)) :
{
name = [for sourceVolume in keys(var.container_mounts) :
replace(substr(sourceVolume, 1, length(sourceVolume)), "/", "-")][0]
host = {
sourcePath = [for key, value in var.container_mounts : value][0]
}
}
]
)
Is there a way to improve it? I assume that yes, but running into different scenarios were it's not working.
So after talking with a friend, looks I complicated my life doing what I did.
json = jsonencode(
[
for key, value in var.container_mounts :{
host = { "sourcePath" = value }
name = replace(substr(key, 1, length(key)), "/", "-")
}
]
)
It can be done only with one loop.

Dynamic JSON in Terraform

I am using Terraform to Invoke a lambda function, and need to pass an input JSON which includes a list of string values.
data "aws_lambda_invocation" "invo6" {
function_name = "my_function"
input = <<JSON
{
"pairs":[
{
"principal":"arn:aws:iam::12345678901:role/myRole",
"databases":[
"my_db_apple", "my_db_banana", "my_db_orange"
]
}
]
}
JSON
}
Instead of hard-coding these database names, I want to pull in from a map that already exists elsewhere in my tf files.
variable "gluedb_map" {
type = map(map(string))
default = {
"apple" = {
description = "my apple db"
catalog = ""
location_uri = "s3://mybucket/"
params = ""
}
"banana" = {
description = "my banana db"
catalog = ""
location_uri = "s3://anotherpath/"
params = ""
}
I tried swapping out the 'databases' code for this :
input = <<JSON
{
"pairs":[
{
"principal":"arn:aws:iam::12345678901:role/myRole",
${jsonencode("databases": [for each in var.gluedb_map : "my_db_${each}"], )}
}
]
}
JSON
but i then get error :
A comma is required to separate each function argument from the next.
Can anyone spot where I'm going wrong ?
Thanks
If you're just interested in accessing the keys of the map then you can use the keys function to return a list of keys. You can then combine that with formatlist to interpolate each list item with a string.
I'd also recommend using a HCL map for the wider data structure and then encoding to JSON rather than trying to JSON encode a section of it and having to mangle things to get it in a suitable shape.
A fully worked example then looks something like this:
variable "gluedb_map" {
type = map(map(string))
default = {
"apple" = {
description = "my apple db"
catalog = ""
location_uri = "s3://mybucket/"
params = ""
}
"banana" = {
description = "my banana db"
catalog = ""
location_uri = "s3://anotherpath/"
params = ""
}
}
}
output "json" {
value = jsonencode({
pairs: [
{
principal = "arn:aws:iam::12345678901:role/myRole"
databases = formatlist("my_db_%s", keys(var.gluedb_map))
}
]
})
}
Applying this will output the following:
json = {"pairs":[{"databases":["my_db_apple","my_db_banana"],"principal":"arn:aws:iam::12345678901:role/myRole"}]}
You can try to use keys, formatlist and join to get:
${jsonencode("databases": [join("," , formatlist("my_db_%s", keys(var.gluedb_map)) )}

Terraform interpolated values in tag keys

I use Terraform to create VMs with names based on count.index. I would like to create tags where the keys are dynamically based on count.index. I just couldn't get it working.
For VM01, tag should be "PatchCycle01 = centos" & for VM02, it should be "PatchCycle02 = centos".
tags = {
PatchCycle${format("%02d", count.index + 1)} = "CentOS"
}
I tried the above code but it didn't work. Any suggestions?
You just need to properly quote the left hand side (the key of the map) for this to work:
locals {
foo = "foo"
map_test = {
"PatchCycle${local.foo}" = "foo"
}
}
output "foo" {
value = local.map_test
}
Applying the above outputs the following:
foo = {
"PatchCyclefoo" = "foo"
}
So for your example you would do something like this:
tags = {
"PatchCycle${format("%02d", count.index + 1)}" = "CentOS"
}
The key needs to be built with a string template like this:
tags = {
"PatchCycle${format("%02d", count.index + 1)}" = "CentOS"
}
If you are using something earier than Terraform 0.12, you will have to use the map function which is deprecated now:
tags = "${map(
"PatchCycle${format("%02d", count.index + 1)}", "CentOS"}"
)

How do I create RDS parameter groups from an arbitrary number of input parameters?

I have a four different environments:
dev
sit
uat
prod
The parameter group values differ for each environment. Below value will go in variable file for each environment.
Below are the parameters of an example AWS RDS parameter group. In other environments, there may be more or less parameters:
parameter {
name = "character_set_client"
value = "utf8mb4"
}
parameter {
name = "character_set_connection"
value = "utf8mb4"
}
parameter {
name = "character_set_server"
value = "utf8mb4"
}
parameter {
name = "log_bin_trust_function_creators"
value = "1"
}
I'm curious how to represent this in variables and how to write the resources to use those variables to make RDS Parameter Groups with an arbitrary number of parameters.
How do I create RDS parameter groups from an arbitrary number of input parameters?
You can use a dynamic block:
dynamic "parameter" {
for_each = var.parameters
content {
name = parameter.value.name
value = parameter.value.value
}
}
The variable can be a list of maps:
variable "parameters" {
type = list(map(string))
default = []
}
terraform.tfvars:
parameters = [
{
name = "character_set_connection"
value = "utf8mb4"
},
{
name = "character_set_server"
value = "utf8mb4"
},
{
name = "log_bin_trust_function_creators"
value = "1"
}
]

flattening output contents of a composite map

I have two module that output respectively
output "discovery_service_hostname" {
value = "${aws_appmesh_virtual_service.service.name}"
}
and
output "discovery_service_arn" {
value = zipmap( aws_service_discovery_service.sd[*].name, aws_service_discovery_service.sd[*].arn)
}
Both are used in the main script that outputs
output "services" {
value = {
"web" = "${module.web.discovery_service_hostname}"
"wwb-backend" = "${module.web_backend.discovery_service_hostname}"
"wwb-backend-n" = "${module.web_backend_n.discovery_service_hostname}"
}
}
in this case I used the 1st module for web andweb-backend, while I used the 2nd module for web-backend-n
I need to access the service arn via lookup function in a 3rd script, but I would avoid duplicating the whole code to handle the two cases
final output like this
discovery_service = {
"web" = "arn:xxx1"
"web-backend" = "arn:xxx2"
"web-backend-n" = {
"web-backend-n-1" = "arn:xxx3"
"web-backend-n-2" = "arn:xxx4
"web-backend-n-3" = "arn:xxx5"
}
Is there a way to have an output like
discovery_service = {
"web" = "arn:xxx1"
"web-backend" = "arn:xxx2"
"web-backend-n-1" = "arn:xxx3"
"web-backend-n-2" = "arn:xxx4
"web-backend-n-3" = "arn:xxx5"
}
thanks!
I will answer my own question. Solution is to always output a map (even from the module with single outputs) like this:
output "discovery_service_arn" {
value = zipmap( [ aws_service_discovery_service.sd.name ], [ aws_service_discovery_service.sd.arn ])
}
and
output "discovery_service_arn" {
value = zipmap( aws_service_discovery_service.sd[*].name, aws_service_discovery_service.sd[*].arn)
}
then in the final script use merge to get a single map like
output "discovery_service" {
value = merge(
module.web.discovery_service_arn,
module.web_backend.discovery_service_arn,
module.web_backend_n.discovery_service_arn
)
}

Resources