Terraform For Loop To Generate JSON From Map - terraform

Looking for the easiest way in terraform to create a JSON string (preferably using jsonencode) or something similar i've looked at the few examples on the terraform docs but still struggling to nail the exact formatting to get this output right. This should make it pretty easy to keep the dev.env file we use in our docker-compose to transfer nicely into this large array that's used in several of our containers.
locals {
container_envs = {
ENV = "dev"
CONTAINER_TAG = "dev"
}
}
To the following type of output
[{
"name": "ENV",
"value": "dev"
},
{
"name": "CONTAINER_TAG",
"value": "dev"
}
]

I think the following should work (not sure if the order is important or not):
locals {
container_envs = {
ENV = "dev"
CONTAINER_TAG = "dev"
}
}
output "test" {
value = [for k,v in local.container_envs: { name = k, value = v }]
}
which gives:
test = [
{
"name" = "CONTAINER_TAG"
"value" = "dev"
},
{
"name" = "ENV"
"value" = "dev"
},
]

Related

Terraform change nested maps values

I have this local map variable
(some AWS ECS tasks definition configurations I read from .JSON files)
tasks = {
"service1" = {
task_definition = {
"cpu": 128,
"environment": [
{
"name": "DB_HOST",
"value": "X"
}
],
"essential": true,
"healthCheck": {}
}
}
"service2" = {
"task_definition" = ...
}
}
I want to change DB_HOST value based on other module output :
worth noting DB_HOST won’t appear on each service - should be changed/added
Something like that :
tasks.x.task_definition.environment[x].value = module.example.db_host
-> DB_HOST = module.example.db_host
didn’t manage to do it when looping through the map keys…
Thanks in advance !

Appending values to a JSON key with terraform?

I am quite new to terraform. I have a provider that will accept JSON as input.
I have stored JSON config files in my project folder, eg:
{
"id": 58187729,
"name": "My dashboard",
"tags": ["mytag1", "mytag2"]
}
and load them into the resource with the below code:
resource "datadog_monitor_json" "monitor_json" {
for_each = fileset(path.module, "/monitors/*.json")
monitor = file("${path.module}/${each.key}")
}
Is there anyway I can easily append to the "tags" key or will the entire JSON need parsing somehow?
Thanks.
Here is an example on how to add extra tags:
locals {
example = jsondecode(file("${path.module}/myfile.json"))
with_extra_tags = merge(local.example,
{tags = concat(
local.example["tags"],["mytag4", "mytag3"])})
}
gives:
test = {
"id" = 58187729
"name" = "My dashboard"
"tags" = [
"mytag1",
"mytag2",
"mytag4",
"mytag3",
]
}

how do I convert a Terraform map variable into a string?

I'm working on an tf plan what builds a json template and out of a map variable and I'm not quite sure how to use the existing looping, type, list functions to do the work. I know that I cannot pass lists or map to a data "template_file" so my thought was to build the string in a locals or null resource block and then pass that to the template
Variable
variable "boostrap_servers" {
type = map
default = {
"env01" : [
"k01.env01",
"k02.env01"
],
"env02" : [
"k01.env02"
]
}
Desired text
"connections": {
"env01": {
"properties": {
"bootstrap.servers": "k01.env01,k02.env01"
}
},
"env02": {
"properties": {
"bootstrap.servers": "k01.env02"
}
},
You may simply use the jsonencode function and list comprehension for this:
locals {
connections = jsonencode({
for cluster, servers in local.bootstrap_servers :
cluster => {
properties = {
"bootstrap.servers" = join(",", servers)
}
}
})
}
Ok, so the following works but there's a better question: why not just use the jsonencode function to build the json
locals {
clusters = [
for cluster, servers in var.boostrap_servers :
"{\"${cluster}\":{\"properties\":{\"bootstrap.servers\":\"${join(" ,", servers)}\"}}"]
connections = join(",", local.clusters)
}

Terraform - How to convert lists into map (How to fetch AMI tags using terraform)

I am trying to fetch the tags of AMI using AWS CLI and want to reuse the values from the output.
I have a terraform code below which is returning outputs in string format(Maybe not sure of format) which I want to convert into a map object.
variable "ami" {
default = "ami-xxxx"
}
locals {
tags = {
"platform" = lookup(data.local_file.read_tags.content, "platform", "") #Expecting to get platform from Map of read_tags
}
}
data "template_file" "log_name" {
template = "${path.module}/output.log"
}
resource "null_resource" "ami_tags" {
provisioner "local-exec" {
command = "aws ec2 describe-tags --filters Name=resource-id,Values=${var.ami} --query Tags[*].[Key,Value] > ${data.template_file.log_name.rendered}"
}
}
data "local_file" "read_tags" {
filename = "${data.template_file.log_name.rendered}"
depends_on = ["null_resource.ami_tags"]
}
output "tags" {
value = local.tags
}
output "cli-output-tags" {
value = "${concat(data.local_file.read_tags.content)}"
}
output of cli-output-tags is below:
[
[
"ENV",
"DEV"
],
[
"Name",
"Base-AMI"
],
[
"platform",
"Linux"
]
]
How can I convert this output into Map as below using terraform/(jq command), or is there any other way to fetch required values directly from cli-output-tags output:
{
ENV = "DEV",
Name = "Base-AMI",
platform = "Linux"
}
I have also tried changing the CLI command a bit like below but still not able to fetch values as expected:
'Tags[].{Key:Key,Value:Value}'
Resulted below output:
[
{
"Key": "ENV",
"Value": "DEV"
},
{
"Key": "Name",
"Value": "Base-AMI"
},
{
"Key": "platform",
"Value": "Linux"
}
]
You could use zipmap:
output "cli-output-tags" {
value = zipmap(
jsondecode(data.local_file.read_tags.content)[*][0],
jsondecode(data.local_file.read_tags.content)[*][1]
)
}
The code first changes string data from your file to json, then
gets all first elements [*][0] (same for second elements [*][1]), and zips them into map.
How can I convert this output into Map as below
One way would be to use jq as follows (assuming cli-output-tags is the name of the file holding the JSON array of arrays):
jq -r -f '"{", (.[] | "\(.[0]) = \"\(.[1])\""), "}"' cli-output-tags

Is it possible to perform nested iterations in HCL resulting in a flat list without calling flatten?

Is it possible with HCL to have nested iterations returning a flat list(map) without resorting to flatten?
I have this:
locals {
mappings = flatten([
for record_type in var.record_types : [
for host in var.hosts : {
type = record_type,
host = host
}
]
])
}
I would like to remove the need for flatten like this:
locals {
mappings = [
for record_type in var.record_types :
for host in var.hosts : {
type = record_type,
host = host
}
]
}
But it seems like each for .. in must return data.
One alternative I could think of to only have a single for-loop is using setproduct():
variable "record_types" {
default = ["type1", "type2"]
}
variable "hosts" {
default = ["host1", "host2"]
}
locals {
mappings = [
for i in setproduct(var.record_types, var.hosts) : {
type = i[0],
host = i[1],
}
]
}
output "mappings" {
value = local.mappings
}
after terraform apply resulting in:
Outputs:
mappings = [
{
"host" = "host1"
"type" = "type1"
},
{
"host" = "host2"
"type" = "type1"
},
{
"host" = "host1"
"type" = "type2"
},
{
"host" = "host2"
"type" = "type2"
},
]
Of course, the two variables need to be independent sets here.
If you want to support duplicates or have dependent inputs, flatten() with two loops is the way.

Resources