Split nested map - gitlab

I want to split the below mapping structure:
stages= {
non_prod = {
"qual" = "token1"
"dev" = "token2"
}
prod = {
"prod" = "token3"
}
testprod = {
"dev" = "token4"
"prod" = "token5"
}
}
Output should be in the below format:
stages= {
"non_prod" = { "qual" = "token1" }
"non_prod" = { "dev" = "token2" }
"prod" = { "prod" = "token3" }
"testprod" = { "dev" = "token4" }
"testprod" = { "prod" = "token5" }
}
So when the output is processed through a for loop, from "non_prod" = { "qual" = "token1" } it will take te values as :
environment = non_prod
stage = qual
token = token1

Witht he below code you can accomplish to create an iterable that allows you to access the information required.
locals {
stages = {
non_prod = {
"qual" = "token1"
"dev" = "token2"
}
prod = {
"prod" = "token3"
}
testprod = {
"dev" = "token4"
"prod" = "token5"
}
}
solution = flatten([
for env, data in local.stages : {
for stage, token in data : join(".", [env, stage]) => tomap({
"env": env,
"stg": stage,
"tok": token
})
}
])
You can use the merge function and the epsilon operator to surround the output of the flatten function to obtain an iterable map in the way you can use it later as required.
> merge(local.solution...)
{
"non_prod.dev" = {
"env" = "non_prod"
"stg" = "dev"
"tok" = "token2"
}
"non_prod.qual" = {
"env" = "non_prod"
"stg" = "qual"
"tok" = "token1"
}
"prod.prod" = {
"env" = "prod"
"stg" = "prod"
"tok" = "token3"
}
"testprod.dev" = {
"env" = "testprod"
"stg" = "dev"
"tok" = "token4"
}
"testprod.prod" = {
"env" = "testprod"
"stg" = "prod"
"tok" = "token5"
}
}
That is an iterable map that you can access the data through by it keys (env, stg, tok).

Related

Terraform: Nested for expression with no duplicity

I am newbie to Terraform, apologies in advance in case inappropriate logic is used.
There is nested map, want to create new map using existing one.
terraform.tfvars
vpcs_info= {
"devops" = {
main = {
cidr = "10.14.0.0/16"
region = "ap-south-1"
peering = {
creator = {
devops = "poc",
uat = "main"
}
}
},
poc = {
cidr = "10.9.0.0/16"
region = "ap-south-1"
peering = {
creator = {
dev = "main"
}
}
}
}
}
locas.tf
locals {
vpcs_info = {
for vpc, properties in var.vpcs_info.devops:
vpc => {for dst_env, dst_vpc in properties.peering.creator : vpc => {"name": "${dst_env}-${dst_vpc}-vpc", "id": "${timestamp()}" }...}
}
}
Actual output:
{
"main" = {
"main" = [
{
"id" = "2023-02-05T21:23:24Z"
"name" = "devops-poc-vpc"
},
{
"id" = "2023-02-05T21:23:24Z"
"name" = "uat-main-vpc"
},
]
}
"poc" = {
"poc" = [
{
"id" = "2023-02-05T21:23:24Z"
"name" = "dev-main-vpc"
},
]
}
}
If we observe, in the output there is duplicity in KEYS. Need to get below desired output using for expression:
Desired output:
{
"main" = {
{
"id" = "2023-02-05T21:23:24Z"
"name" = "devops-poc-vpc"
},
{
"id" = "2023-02-05T21:23:24Z"
"name" = "uat-main-vpc"
}
}
"poc" = {
{
"id" = "2023-02-05T21:23:24Z"
"name" = "dev-main-vpc"
}
}
}
Note: "id" is sensitive information in actual requirement, to avoid security challenges, "${timestamp()}" is being used here.
You can do that with merge:
vpcs_info = merge([
for vpc, properties in var.vpcs_info.devops: {
for dst_env, dst_vpc in properties.peering.creator:
vpc => {
"name" = "${dst_env}-${dst_vpc}-vpc"
"id" = "${timestamp()}"
}...
}
]...)

Extract Maps from List in Terraform

I have a list of complex maps, like so (I've simplified the maps, but really they're made up more maps):
[
{
"hooha" = {
"foo" = { "something" = "this" }
}
},
{
"woot" = {
"bar" = { "other_things" = "that" }
}
},
]
I need to feed this into jsonencode(), but first have it look like this:
{
"hooha" = {
"foo" = { "something" = "this" }
},
"woot" = {
"bar" = { "other_things" = "that" }
}
}
So from a list of maps, to a map of the maps.
This seemed trivial, but for the life of me, I can't figure it out!
You can use the function merge with a combination of with ... operator.
locals {
my_list = [
{
"hooha" = {
"foo" = { "something" = "this" }
},
"www" = {
"aa" = {"x" = "y"}
}
},
{
"woot" = {
"bar" = { "other_things" = "that" }
}
},
]
}
output "prepare" {
value = merge(local.my_list...)
}
The output will be something like this:
prepare = {
"hooha" = {
"foo" = {
"something" = "this"
}
}
"woot" = {
"bar" = {
"other_things" = "that"
}
}
"www" = {
"aa" = {
"x" = "y"
}
}
}

Dynamically Rebuilding a Terraform Complex Type

I could be going about this the wrong way, but I'm trying to copy a complex type and inject an object at specific point in a list of complex types. I have a working solution but it is not dynamic and I am forced to repeat myself for each point that I'm modifying.
The below works, but is not dynamic. I am required to repeat a block in the conditional merge statement for each property I need to manipulate.
locals {
map_to_merge = {
foo = "bar"
}
original_list = [
{
property_1 = "value"
property_2 = {
"nested" = {
string_property = "string"
map_property_1 = {
a = "b"
list = []
}
map_property_2 = {
x = "y"
}
}
}
}
]
modified_list = [
for item in local.original_list : merge(item, {property_2 = {
for key, value in item.property_2 : key => merge(value,
contains(keys(value), "map_property_1") ? {
map_property_1 = merge(value.map_property_1, local.map_to_merge)
} : {},
contains(keys(value), "map_property_2") ? {
map_property_2 = merge(value.map_property_2, local.map_to_merge)
} : {}
)
}})
]
}
output "test_original" {
value = local.original_list
}
output "test_modified" {
value = local.modified_list
}
The output is,
+ test_modified = [
+ {
+ property_1 = "value"
+ property_2 = {
+ nested = {
+ map_property_1 = {
+ a = "b"
+ foo = "bar"
+ list = []
}
+ map_property_2 = {
+ foo = "bar"
+ x = "y"
}
+ string_property = "string"
}
}
},
]
+ test_original = [
+ {
+ property_1 = "value"
+ property_2 = {
+ nested = {
+ map_property_1 = {
+ a = "b"
+ list = []
}
+ map_property_2 = {
+ x = "y"
}
+ string_property = "string"
}
}
},
]
What I'd like to do is something like below as it allows me not to repeat the conditional merge statement for each property I want to merge with.
locals {
map_to_merge = {
foo = "bar"
}
original_list = [
{
property_1 = "value"
property_2 = {
"nested" = {
string_property = "string"
map_property_1 = {
a = "b"
list = []
}
map_property_2 = {
x = "y"
}
}
}
}
]
modified_list = [
for item in local.original_list : merge(item, {property_2 = {
for nested, definition in item.property_2 : nested => merge(definition, {
for key, value in definition : key => can(tomap(value))
? merge(
value,
key == "map_property_1" || key == "map_property_2"
? local.map_to_merge
: {}
)
: value
})
}})
]
}
output "test_original" {
value = local.original_list
}
output "test_modified" {
value = local.modified_list
}
But I am getting an error,
Error: Inconsistent conditional result types
on test.tf line 41, in locals:
40: for key, value in definition : key => can(tomap(value))
41: ? merge(
42: value,
43: key == "map_property_1" || key == "map_property_2"
44: ? { foo = "bar" }
45: : {}
46: )
47: : value
The true and false result expressions must have consistent types. The given
expressions are object and object, respectively.
Is there a dynamic/better way to do this or do I need to repeat the block for each property that needs to be manipulated?
Turns out that switching from the use of can to try enables this to run as I'd expect.
locals {
map_to_merge = {
foo = "bar"
}
original_list = [
{
property_1 = "value"
property_2 = {
"nested" = {
string_property = "string"
map_property_1 = {
a = "b"
list = []
}
map_property_2 = {
x = "y"
}
}
}
}
]
modified_list = [
for item in local.original_list : merge(item, {property_2 = {
for nested, definition in item.property_2 : nested => merge(definition, {
for key, value in definition : key => try(
merge(
value,
key == "map_property_1" || key == "map_property_2"
? local.map_to_merge
: {}
),
value
)
})
}})
]
}
output "test_original" {
value = local.original_list
}
output "test_modified" {
value = local.modified_list
}
Here is the output,
+ test_modified = [
+ {
+ property_1 = "value"
+ property_2 = {
+ nested = {
+ map_property_1 = {
+ a = "b"
+ foo = "bar"
+ list = []
}
+ map_property_2 = {
+ foo = "bar"
+ x = "y"
}
+ string_property = "string"
}
}
},
]
+ test_original = [
+ {
+ property_1 = "value"
+ property_2 = {
+ nested = {
+ map_property_1 = {
+ a = "b"
+ list = []
}
+ map_property_2 = {
+ x = "y"
}
+ string_property = "string"
}
}
},
]

is there a way to concat tuple in terraform?

variable "query_param" {
type = list(string)
default = [
"hello",
"one",
"two"
]
}
locals {
common_tags = flatten([
for i in var.query_param: {
"method.request.querystring.${i}" = false
}
])
}
output name {
value = local.common_tags
description = "description"
}
I have this code to iterate the variable(list) to locals (map). I got the below output which is not desired.
name = [
{
"method.request.querystring.hello" = false
},
{
"method.request.querystring.one" = false
},
{
"method.request.querystring.two" = false
},
]
The desired output is getting all the values in a single array. As shown below.
name = [
{
"method.request.querystring.hello" = false,
"method.request.querystring.one" = false,
"method.request.querystring.two" = false
}
]
Is there any way to achieve this?
I think you would want something like this:
locals {
common_tags = {
for i in var.query_param :
"method.request.querystring.${i}" => false
}
}
The output of the code above will be as follows:
name = {
"method.request.querystring.hello" = false
"method.request.querystring.one" = false
"method.request.querystring.two" = false
}
Please note, the name itself is not an array in this case. If you want it to be, you can do the following:
locals {
common_tags = [{
for i in var.query_param :
"method.request.querystring.${i}" => false
}]
}
The output will be:
name = [
{
"method.request.querystring.hello" = false
"method.request.querystring.one" = false
"method.request.querystring.two" = false
}
]

Converting a list(map(list(map(string)))) into a map(list(map(string))) with terraform

Hi there is there any neat way to convert a list(map(list(map(string)))) into a map(list(map(string))) with terraform .
Here's what my input structure looks like
`variable "data" {
type = list(map(list(map(string))))
default = [
{
secrets = [
{
secret-name = "secret1"
secret-label = "label1"
},
{
secret-name = "secret2"
secret-label = "label2"
}
],
config-maps = [
{
config-map-name = "conf1"
java-options = "-Xmx256m"
config-label = "develop"
}
]
},
{
secrets = [
{
secret-name = "secret3"
secret-label = "label3"
}
],
config-maps = [
{
config-map-name = "conf2"
java-options = "-Xmx256m"
config-label = "develop"
}
]
}
]
}`
and here's what i want it to look like
`data = {
secrets = [
{
secret-name = "secret1"
secret-label = "label1"
},
{
secret-name = "secret2"
secret-label = "label2"
},
{
secret-name = "secret3"
secret-label = "label3"
},
],
config-maps = [
{
config-map-name = "conf1"
java-options = "-Xmx256m"
config-label = "develop"
},
{
config-map-name = "conf2"
java-options = "-Xmx256m"
config-label = "develop"
}
]
}
Can you guys help me achieve this please ?
Ps : preferably in the locals part
Then, how about this?
locals {
values = flatten([
for d in var.data : [
for _, v in d : v
]
])
keys = flatten([
for d in var.data : [
for k, v in d : [
for _ in v : k
]
]
])
}
output data {
value = {
for k in distinct(local.keys) :
k => matchkeys(local.values, local.keys, [k])
}
}
I hope this will help.

Resources