Terraform merge values in maps into sinlge using key lookup - terraform

I have a locals block which returns data in list of maps
ids = [
{
"d81f6779-031f-4aa6-8e88-3242f65e35c0" = "478da44dc0aa8b0cdce5e7448c9509a5095a0f93"
},
{
"d81f6779-031f-4aa6-8e88-3242f65e35c0" = "131e25de9ffd028fc9dab497f537eecb6d1b0faf"
},
{
"e2e6deb4-8512-4089-b2ec-ef77447dabb7" = "f8ea0d6234a8ee2858a3ae04952c6c974337cf6a"
},
{
"e2e6deb4-8512-4089-b2ec-ef77447dabb7" = "143b826f93e78d5a18ac9276972e80414262755e"
},
{
"e2e6deb4-8512-4089-b2ec-ef77447dabb7" = "e7fe936edcc34c258c35aa19b9c702e553e3b265"
},
]
I am looking for an output like below. any function I can use or have to write some for loops?
ids =
{
"d81f6779-031f-4aa6-8e88-3242f65e35c0" = ["478da44dc0aa8b0cdce5e7448c9509a5095a0f93", "131e25de9ffd028fc9dab497f537eecb6d1b0faf"],
"e2e6deb4-8512-4089-b2ec-ef77447dabb7" = ["f8ea0d6234a8ee2858a3ae04952c6c974337cf6a", "143b826f93e78d5a18ac9276972e80414262755e","e7fe936edcc34c258c35aa19b9c702e553e3b265"]
}

This worked by manipulating local variable with grouping mode as described in documentation. I didn't had to write another block.
locals {
ids = {for vfc in data.vra_fabric_compute.this : vfc.custom_properties.vcUuid => vfc.id... }
}

Related

Terraform map transformation

I have been banging my head for a few days trying to do this. I know I have probably seen it before but google is failing me.
I have created a local from a data source.:
locals {
my_new_map = [for group in data.okta_groups.aws_groups.groups: {
id = group.id
name = group.name
} if contains(local.groups_not_to_check, group.name) == false]
}
the result is
[
{
id = <group1id>
name = <group1name>
},
{
id = <group2id>
name = <group2name>
},
{
...
},
]
What i need is to transform it to this:
[
<group1name> = <group1id>,
<group2name> = <group2id>,
...
]
groupnames and ids are all unique so can be used as keys
Don’t know if it is possible in my for statement or not but am willing to create a local to do it.
Any help would be awesome.
Have tried many combinations of the FOR expression but to no avail.
Maybe i am missing a function of some sort that could help or maybe i just haven't hit the correct way to output the For expression.
The following should do what you want:
locals {
my_new_map = {for group in data.okta_groups.aws_groups.groups:
group.name => group.id if contains(local.groups_not_to_check, group.name) == false }
}

Remove duplicates from output

I have the following output:
output "regions_data" {
value = regions({
for region, data in var.regions :
regions => "${region}/${data.postcode}"
})
}
Which contains duplicates like(it is intentional):
regions = {
reg1 = {
postcode = "1"
},
reg1 = {
postcode = "1"
},
reg2 = {
postcode = "2"
}
}
How can I remove the duplicates from the output?
Your code does not comply to the basic rules of maps or objects. Nor there is any regions function you use in the code. The provided code is not a proper Terraform syntax.
I believe however, you might have meant the following example:
variable "regions" {
default = {
reg1 = [
{
postcode = 1
area = "oak-county"
},
{
postcode = 2
area = "birch-county"
}
],
reg2 = [
{
postcode = 1
area = "fir-county"
},
{
postcode = 2
area = "pine-county"
}
],
}
}
In a case, when the two maps have the same keys, you can use flatten to break up everything to pieces, then rejoin everything back together:
locals {
flatten = flatten([
for region_key, region in var.regions : [
for area in region :
{
key = "${region_key}-${area.postcode}"
value = area.area
}
]
])
}
output "flattened_regions" {
value = local.flatten
}
output "remap" {
value = { for key, data in local.flatten :
data.key => data.value
}
}
Even if the code above doesn't exactly fit your case, please experiment in a similar manner - or, provide more complete example of variables you have and the outcome you need.
Source: https://www.terraform.io/language/functions/flatten
Probably what you want
I have no idea what you've meant by regions in value = regions({ but I assume that this code will do what you want:
locals {
regions = {
reg1 = {
postcode = "1"
},
reg1 = {
postcode = "1"
},
reg2 = {
postcode = "2"
}
}
}
output "regions_data" {
value = {
for region, data in local.regions :
region => "${region}/${data.postcode}"
}
}
Keep in mind that I replaced var with local to have one file.
And output of such is:
regions_data = {
"reg1" = "reg1/1"
"reg2" = "reg2/2"
}
Thought be warned that it will use one of keys. It doesn't check for duplicates. It just takes first one.
But why you shouldn't want it
This solution is quite bad for multiple reasons:
You provide variables - why the heck would you put duplicates? :)
As I said this merge will not necessarily provide the output you what
If var is provided by some terraform code (e.g. this regions_data is in module) then logic of merging should be done outside of module and probably terraform's own merge would be the answer.

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.

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
)
}

Look for the resource to match a key word

Suppose I have two kineses, I'd like to get the one of them with the key word _consumer.
variable "kinesis" {
default = ["kinesis_publisher", "kinesis_consumer"]
}
resource "aws_kinesis_stream" "test_stream" {
count = "${length(var.kinesis)}"
name = "${var.kinesis[count.index]}"
shard_count = 1
retention_period = 48
shard_level_metrics = [
"IncomingBytes",
"OutgoingBytes",
]
tags = {
Environment = "test"
}
}
How do I get consumer arn only?
output "kinesis_consumer_arn" {
value = "??? lookup or matchkeys with _consumer ???"
}
It is not always the same sequence and will be many kinesis. So I can't use 0 or 1 directly.
You may create modules for each kafka stream , thereby giving more control on variables passed into and derived from the resources.
module "kinesis_publisher" {
source = "../modules/test_stream"
stream_name = "kinesis_publisher"
}
module "kinesis_consumer" {
source = "../modules/test_stream"
stream_name = "kinesis_consumer"
}
And then output can be filtered on basis of modules.
output "kinesis_consumer_arn" {
value = "{module.kinesis_consumer.arn}"
}

Resources