Accessing the first object in a tuple using terraform - json

I am trying to access the first key on a given tuple. The key's name is dynamic and may change so it cannot be accessed using a static value and has to be done either through a for loop or through the use of terraform fuctions.
I've created a small local resource that outputs the following section
skip_empty_mails = { for key, value in local.j.settings.tasks : key => value.email_notifications if value.email_notifications != {} }
The output sent back is
{
"3" = {
"on_start" = [
"foo1#aligntech.com",
"foo2#aligntech.com",
"foo3#aligntech.com",
"foo4#aligntech.com",
]
}
"4" = {
"no_alert_for_skipped_runs" = false
"on_start" = [
"foo21#aligntech.com",
"foo22#aligntech.com",
"foo23#aligntech.com",
"foo24#aligntech.com",
]
]
"on_start" = [
"foo21#aligntech.com",
"foo22#aligntech.com",
"foo23#aligntech.com",
"foo24#aligntech.com",
]
"on_success" = [
"foo21#aligntech.com",
"foo22#aligntech.com",
"foo23#aligntech.com",
"foo24#aligntech.com",
]
}
}
As seen above the key that holds all the values needs to be accessed in a way that will give me the ability to attach it to a string and use string.on_start to pull its values.
The issue is that our key's name is dynamic and may vary.
I've tried following the terraform function documentation But haven't found anything that might be of use in this case.
You may be able to replicate using the following code
locals {
json = {
"3" = {
"on_start" = [
"foo1#aligntech.com",
"foo2#aligntech.com",
"foo3#aligntech.com",
"foo4#aligntech.com",
]
},
"4" = {
"no_alert_for_skipped_runs" = false
"on_failure" = [
"foo21#aligntech.com",
"foo22#aligntech.com",
"foo23#aligntech.com",
"foo24#aligntech.com",
]
"on_start" = [
"foo21#aligntech.com",
"foo22#aligntech.com",
"foo23#aligntech.com",
"foo24#aligntech.com",
]
"on_success" = [
"foo1#foo.com",
"foo2#foo.com",
"foo3#foo.com",
"foo4#foo.com",
]
}
}
}

You can try with a combination of values, element or flatten see the documentation:
https://developer.hashicorp.com/terraform/language/functions/values
https://developer.hashicorp.com/terraform/language/functions/element
https://developer.hashicorp.com/terraform/language/functions/flatten
Below are samples:
First key extraction
locals {
json = {
"4" = {
"no_alert_for_skipped_runs" = false
"on_failure" = [
"foo1#foo.com",
"foo2#foo.com",
]
"on_start" = [
"foo1#foo.com",
"foo2#foo.com",
]
}
}
}
output "data" {
value = element(values(local.json), 1).on_start
}
the Terraform plan will be:
Changes to Outputs:
+ data = [
+ "foo1#foo.com",
+ "foo2#foo.com",
]
Extract and combine on_start item from all
locals {
json = {
"3" = {
"on_start" = [
"foo1#aligntech.com",
"foo2#aligntech.com",
]
},
"4" = {
"on_start" = [
"foo1#foo.com",
"foo2#foo.com",
]
}
}
}
output "data" {
value = flatten(values(local.json)[*].on_start)
}
the Terraform plan will be:
Changes to Outputs:
+ data = [
+ "foo1#aligntech.com",
+ "foo2#aligntech.com",
+ "foo1#foo.com",
+ "foo2#foo.com",
]

Related

Parsing json in terraform locals

I have another question about JSON parsing. Imagine I have this JSON:
{
"all_dogs" :[
{
"name": "foo",
"groups": ["morning", "evening"]
},
{
"name": "bar",
"groups": ["evening", "saturday"]
},
{
"name": "feet",
"groups": ["afternoon"]
}
]
}
I can extract all the groups like this:
locals {
all_dogs = jsondecode(file("${path.module}/dogs.json"))
all_groups = toset(flatten(local.all_dogs.all_dogs[*].groups))
}
Now, I’m trying to create a MAP. Each group is a key of the map and values of the maps are the different dogs in that group.
So I would like to create a map like this:
afternoon = [feet]
evening= [foo, bar]
morning= [foo]
saturday= [bar]
I'm trying with something like this and I tried several options... But I can't make it work.
output "ex" {
value = flatten([
for group in local.all_groups: [
for dog in local.all_dogs : {
group = group
dog = dog
}
]
]
)
}
Later on, I would like to use that map to provision some resources. Is this eventually possible?
locals {
all_dogs = jsondecode(file("${path.module}/dogs.json"))
groups = flatten([for d in local.all_dogs.all_dogs : [for g in d.groups : { key : g, value : d.name }]])
}
The value for local groups will be a list of tuples and it will look something like this:
groups = [
{
"key" = "morning"
"value" = "foo"
},
{
"key" = "evening"
"value" = "foo"
},
{
"key" = "evening"
"value" = "bar"
},
{
"key" = "saturday"
"value" = "bar"
},
{
"key" = "afternoon"
"value" = "feet"
},
]
Now we have to create a map from this list:
output "my_map" {
value = {
for g in local.groups : g.key => g.value...
}
}
This will produce the following output:
my_map= {
"afternoon" = [
"feet",
]
"evening" = [
"foo",
"bar",
]
"morning" = [
"foo",
]
"saturday" = [
"bar",
]

Modifying JSON in Groovy (or JOLT)

I've a simple JSON look like:
{
"account_login" : "google#gmail.com",
"view_id" : 1868715,
"join_id" : "utm_campaign=toyota&utm_content=multiformat_sites&utm_medium=cpc&utm_source=facebook",
"start_date" : "2020-02-03",
"end_date" : "2020-08-30"
}
With following Groovy script (from this answer):
def content = """
{
"account_login" : "google#gmail.com",
"view_id" : 1868715,
"join_id" : "utm_campaign=toyota&utm_content=multiformat_sites&utm_medium=cpc&utm_source=facebook",
"start_date" : "2020-02-03",
"end_date" : "2020-08-30"
}
"""
def slurped = new JsonSlurper().parseText(content)
def builder = new JsonBuilder(slurped)
builder.content.join_id = builder.content.join_id.split("\\s*&\\s*") //# to array
.collectEntries{
//# convert each item to map entry
String[] utmMarks = it.trim().split("\\s*=\\s*")
utmMarks[0] = [
"utm_medium" : "ga:medium",
"utm_campaign" : "ga:campaign",
"utm_source" : "ga:source",
"utm_content" : "ga:adContent",
"utm_term" : "ga:keyword",
].get( utmMarks[0] )
utmMarks
}
.findAll{
k,v-> k && v!=null //# filter out empty/null keys
}
//builder.content.filters = ...
println(builder.toPrettyString())
I'll get:
{
"account_login": "google#gmail.com",
"view_id": 1868715,
"join_id": {
"ga:campaign": "toyota",
"ga:adContent": "multiformat_sites",
"ga:medium": "cpc",
"ga:source": "facebook"
},
"start_date": "2020-02-03",
"end_date": "2020-08-30"
}
I want to update this script (or write new) and add new property: array filters to modified json above. Expected output:
{
"account_login":"google#gmail.com",
"view_id":1868715,
"join_id":{
"ga:campaign":"toyota",
"ga:adContent":"multiformat_sites",
"ga:medium":"cpc",
"ga:source":"facebook"
},
"start_date":"2020-02-03",
"end_date":"2020-08-30",
"converted_utm_marks":"ga:campaign=toyota&ga:adContent=multiformat_sites&ga:medium=cpc&ga:source=facebook",
"filters":[
{
"dimensionName":"ga:medium",
"operator":"EXACT",
"expressions":[
"cpc"
]
},
{
"dimensionName":"ga:adContent",
"operator":"EXACT",
"expressions":[
"multiformat_sites"
]
},
{
"dimensionName":"ga:campaign",
"operator":"EXACT",
"expressions":[
"toyota"
]
},
{
"dimensionName":"ga:source",
"operator":"EXACT",
"expressions":[
"facebook"
]
}
]
}
But the problem is that the set of filters for each JSON will be different. This set depends directly on the join_id set. If JSON join_id will contain:
"join_id": {
"ga:campaign": "toyota",
"ga:keyword": "car"
}
filters array should be:
[
{
"dimensionName":"ga:campaign",
"operator":"EXACT",
"expressions":[
"toyota"
]
},
{
"dimensionName":"ga:keyword",
"operator":"EXACT",
"expressions":[
"car"
]
}
]
operator is always equals EXACT. Property dimensionName - is a join_id.propety name. Expressions is a join_id.property value. So, property filters based on join_id and I need to loop through join_id property and build filters array with described structure. How to achieve expected output? JOLT configuration appreciated also.
I can't even simple iterate through join_id map:
slurped.join_id.each { println "Key: $it.key = Value: $it.value" }
I got the error:
/home/jdoodle.groovy: 24: illegal colon after argument expression;
solution: a complex label expression before a colon must be parenthesized # line 24, column 28.
.collect { [it.ga:campaign] }
UPDATE
I found out how to build this array:
def array =
[
filters: slurped.join_id.collect {key, value ->
[
dimensionName: key,
operator: "EXACT",
expressions: [
value
]
]
}
]
Seems like i got it:
def slurped = new JsonSlurper().parseText(content)
def builder = new JsonBuilder(slurped)
builder.content.filters = builder.content.join_id.collect {key, value ->
[
dimensionName: key,
operator: "EXACT",
expressions: [
value
]
]
}
Are there any better solutions?
def slurped = new JsonSlurper().parseText(content)
def builder = new JsonBuilder(slurped)
builder.content.filters = builder.content.join_id.collect {key, value ->
[
dimensionName: key,
operator: "EXACT",
expressions: [
value
]
]
}

Reading the JSON hierarchy (Number of subnodes can be any number) file using Powershell

Reading the JSON hierarchy (Number of subnodes can be any number) file using Powershell.
I could do it for one level but need the same for N number of loops and nested loops
Need result in Tree view, if not possible then Tabular way with the title on top of the root table
Below is the script so far I have developed:
$Filepath = 'C:\Users\Learner\Test2.JSON'
$JsonContent=gc $Filepath | ConvertFrom-Json
$RootNodeCount=$JsonContent.psobject.Properties.name.Count
$RootNodeName=$JsonContent.psobject.Properties.name
foreach ($Name in $RootNodeName) {Echo $Name--> $JsonContent.$Name}
JSON File
{
"foundation": {
"network": {
"resource_group_name": "rg-network-e2",
"name": "nonprodvnet-e2"
},
"diagnostics": {
"resource_group_name": "rg-mgmt-cu",
"storage_account_name": "Sreacnt-e2"
},
"log_analytics": {
"resource_group_name": "rg-mgmt-cu",
"workspace_name": "la-sap-e2"
},
"recovery_vault": {
"resource_group_name": "rg-mgmt-cu",
"name": "rsv-sap-e2"
},
"windows_domain": {
"domain_name": "dmn.local",
"ou_path": "CN=Computers,DC=example,DC=com",
"domain_user": "svc_domainjoin#dmn.local",
"domain_password": "Pwd01"
}
},
"control_flags": {
"enable_boot_diagnostics": true,
"enable_oms": false,
"enable_backup": true,
"windows_domain_join": false
},
"deployment": {
"resource_group": {
"name": "rg-sap-501-e2",
"location": "eastus2"
},
"tags": {
"owner": "for SAP"
},
"os_account": {
"admin_username": "locadm"
},
"proximity_placement_groups": {
"ppg-501": {}
},
"availability_sets": {
"avset-cs-501": {
"ppg_name": "ppg-501"
},
"avset-app-501": {
"ppg_name": "ppg-501"
},
"avset-db-501": {
"ppg_name": "ppg-501"
}
},
"load_balancers": {
"ilb-sap-501": {
"dv2ascs": {
"ip_address": "192.0.18.20",
"probe_port": 62000,
"subnet": "app"
},
"dv2-ers": {
"ip_address": "192.0.18.21",
"probe_port": 62102,
"subnet": "app"
}
},
"ilb-db-501": {
"dv2-hana": {
"ip_address": "192.0.17.12",
"probe_port": 62503,
"subnet": "db"
}
}
},
"server_groups": {
"ascs": {
"os_type": "linux",
"sku": "Standard_E4s_v3",
"availability_set": "avset-cs-501",
"backup_policy": "sap",
"ppg_name": "ppg-501",
"lb_refs": [
"dv2-ascs",
"dv2-ers"
],
"os_disk_size": "30",
"enable_accelerated_networking": true,
"hosts": {
"vcs501-01": {
"nics": [
[
"192.0.18.18"
]
]
},
"vcs501-02": {
"nics": [
[
"192.0.18.19"
]
]
}
},
"subnet": "app",
"image_details": {
"resource_id": null,
"marketplace_reference": {
"publisher" : "SUSE",
"offer" : "sles-sap-15-sp1",
"sku" : "gen1",
"version": "latest"
}
},
"disks": [
{
"name": "usrsap",
"disk_size": "128",
"number_of_disks": 1
}
]
},
"apps": {
"type": "app_linux",
"os_type": "linux",
"sku": "Standard_E4s_v3",
"availability_set": "avset-app-501",
"backup_policy": "sap",
"ppg_name": "ppg-501",
"enable_accelerated_networking": true,
"os_disk_size": "30",
"hosts": {
"vas501-01": {
"nics": [
[
"192.0.18.16",
"192.0.18.26"
]
]
},
"vas501-02": {
"nics": [
[
"192.0.18.17",
"192.0.18.27"
]
]
}
},
"subnet": "app",
"image_details": {
"resource_id": null,
"marketplace_reference": {
"publisher" : "SUSE",
"offer" : "sles-15-sp1",
"sku" : "gen1",
"version": "latest"
}
},
"disks": [
{
"name": "usrsap",
"disk_size": "128",
"number_of_disks": 1
}
]
},
"hana": {
"os_type": "linux",
"sku": "Standard_E32s_v3",
"availability_set": "avset-db-501",
"backup_policy": "sap",
"ppg_name": "ppg-501",
"os_disk_size": "30",
"subnet": "db",
"lb_refs": [
"dv2-hana"
],
"hosts": {
"vhs501-01": {
"nics": [
[
"192.0.17.10"
]
]
},
"vhs501-02": {
"nics": [
[
"192.0.17.11"
]
]
}
},
"image_details": {
"resource_id": null,
"marketplace_reference": {
"publisher" : "SUSE",
"offer" : "sles-sap-15-sp1",
"sku" : "gen1",
"version": "latest"
}
},
"disks": [
{
"name": "usrsap",
"disk_size": "128",
"number_of_disks": 1
},
{
"name": "hanashared",
"disk_size": "128",
"number_of_disks": 1
},
{
"name": "hanadata",
"disk_size": "128",
"number_of_disks": 3
},
{
"name": "hanalog",
"disk_size": "128",
"number_of_disks": 2
},
{
"name": "hanabackup",
"disk_size": "512",
"number_of_disks": 1
}
]
}
}
}
}
You are not really reading a Json file but actually the hierarchy of a PowerShell object which might hold a lot of (.Net) object types but in this case it is limited by the original Json structure which only consists out of three major types:
Arrays, containing multiple child objects
PSCustomObjects (unless you use the ConvertFrom-Json -AsHashTable parameter), containing child objects assigned to a specific name.
Primitives (including strings), which is basically an object ("leaf") without children.
To best way to read through a hierarchic structure of an unknown depth is to use a recursive function, meaning a function that calls itself. To give you an example for your specific question:
Function Show-Object ($Object, $Depth = 0, $Name) {
$Indent = if ($Depth++) { " " * ($Depth - 2) }
if ($Object -is [Array]) {
If ($Name) { "$Indent$Name =" }
foreach ($Item in $Object) {
Show-Object $Item $Depth
}
}
elseif ($Object -is [PSCustomObject]) {
If ($Name) { "$Indent$Name =" }
foreach ($Name in $Object.PSObject.Properties.Name) {
Show-Object $Object.$Name $Depth $Name
}
}
else {
if ($Name) { "$Indent$Name = $Object" } else { "$Indent$Object" }
}
}
As you can see, where the structure might any child objects (in case of an [Array] or an [PSCustomObject]), the Show-Object function is calling itself with a child object as the new input object, and some additional parameters as to e.g. keep track of the current $Depth.
You can call the function simply like:
Show-Object $JsonContent
Or export it to a file like:
Show-Object $JsonContent | Out-File .\text1.txt
For your $JsonContent it will result in:
foundation =
network =
resource_group_name = rg-network-e2
name = nonprodvnet-e2
diagnostics =
resource_group_name = rg-mgmt-cu
storage_account_name = Sreacnt-e2
log_analytics =
resource_group_name = rg-mgmt-cu
workspace_name = la-sap-e2
recovery_vault =
resource_group_name = rg-mgmt-cu
name = rsv-sap-e2
windows_domain =
domain_name = dmn.local
ou_path = CN=Computers,DC=example,DC=com
domain_user = svc_domainjoin#dmn.local
domain_password = Pwd01
control_flags =
enable_boot_diagnostics = True
enable_oms = False
enable_backup = True
windows_domain_join = False
deployment =
resource_group =
name = rg-sap-501-e2
location = eastus2
tags =
owner = for SAP
os_account =
admin_username = locadm
proximity_placement_groups =
ppg-501 =
availability_sets =
avset-cs-501 =
ppg_name = ppg-501
avset-app-501 =
ppg_name = ppg-501
avset-db-501 =
ppg_name = ppg-501
load_balancers =
ilb-sap-501 =
dv2ascs =
ip_address = 192.0.18.20
probe_port = 62000
subnet = app
dv2-ers =
ip_address = 192.0.18.21
probe_port = 62102
subnet = app
ilb-db-501 =
dv2-hana =
ip_address = 192.0.17.12
probe_port = 62503
subnet = db
server_groups =
ascs =
os_type = linux
sku = Standard_E4s_v3
availability_set = avset-cs-501
backup_policy = sap
ppg_name = ppg-501
lb_refs =
dv2-ascs
dv2-ers
os_disk_size = 30
enable_accelerated_networking = True
hosts =
vcs501-01 =
nics =
192.0.18.18
vcs501-02 =
nics =
192.0.18.19
subnet = app
image_details =
resource_id =
marketplace_reference =
publisher = SUSE
offer = sles-sap-15-sp1
sku = gen1
version = latest
disks =
name = usrsap
disk_size = 128
number_of_disks = 1
apps =
type = app_linux
os_type = linux
sku = Standard_E4s_v3
availability_set = avset-app-501
backup_policy = sap
ppg_name = ppg-501
enable_accelerated_networking = True
os_disk_size = 30
hosts =
vas501-01 =
nics =
192.0.18.16
192.0.18.26
vas501-02 =
nics =
192.0.18.17
192.0.18.27
subnet = app
image_details =
resource_id =
marketplace_reference =
publisher = SUSE
offer = sles-15-sp1
sku = gen1
version = latest
disks =
name = usrsap
disk_size = 128
number_of_disks = 1
hana =
os_type = linux
sku = Standard_E32s_v3
availability_set = avset-db-501
backup_policy = sap
ppg_name = ppg-501
os_disk_size = 30
subnet = db
lb_refs =
dv2-hana
hosts =
vhs501-01 =
nics =
192.0.17.10
vhs501-02 =
nics =
192.0.17.11
image_details =
resource_id =
marketplace_reference =
publisher = SUSE
offer = sles-sap-15-sp1
sku = gen1
version = latest
disks =
name = usrsap
disk_size = 128
number_of_disks = 1
name = hanashared
disk_size = 128
number_of_disks = 1
name = hanadata
disk_size = 128
number_of_disks = 3
name = hanalog
disk_size = 128
number_of_disks = 2
name = hanabackup
disk_size = 512
number_of_disks = 1

Terraform dynamic json content

Environment : Terraform 0.12.X
If variable var.cloudfront_distributionId is not empty then this should add cloudfront policy
else should not
locals {
codebuild_policy = {
Version = "2012-10-17"
Statement = [
{
Effect = "Allow",
Resource = [
"*"
],
Action = [
"ec2:*"
]
},
{
#count = length(var.cloudfront_distributionId) != 0 ? 1 : 0
effect = "Allow",
Action = [
"cloudfront:ListInvalidations",
"cloudfront:GetInvalidation",
"cloudfront:CreateInvalidation"
],
Resource = [
for cf_id in var.cloudfront_distributionId:
"arn:aws:cloudfront::xxxxxxxx:distribution/${cf_id}"
]
}
]
}
}
resource "aws_iam_role_policy" "codebuild" {
count = local.env_build_resource.count
name = "${var.namespace}-${var.environment}-${var.service_name}"
role = element(aws_iam_role.codebuild.*.name, 0)
policy = jsonencode(local.codebuild_policy)
}
Example 1
cloudfront_distributionId = ["1", "2"]
then
Resource = ["1", "2"]. which is working
Example 2
cloudfront_distributionId = [] is empty
It is creating, werein it should not get created.
+ {
+ Action = [
+ "cloudfront:ListInvalidations",
+ "cloudfront:GetInvalidation",
+ "cloudfront:CreateInvalidation",
]
+ Resource = []
+ count = 0
+ effect = "Allow"
},
How can I achieve this ?
There are some parts missing in your code, thus its difficult to verify the answer form me, but I think the following could be attempted:
resource "aws_iam_role_policy" "codebuild" {
# remaining attributes
policy = length(var.cloudfront_distributionId) > 0 ? jsonencode(local.codebuild_policy): null
}
This is not really a JSON-specific question but rather a general question about conditionally including items in a list, because from Terraform's perspective that Statement attribute of your object is just a normal list value.
One way to think of this is as the concatenation of two lists, one of which might be empty. We can concatenate lists using the concat function, and then use normal conditional expressions to decide the length of the second list to concatenate:
locals {
codebuild_policy = {
Version = "2012-10-17"
Statement = concat(
[
{
Effect = "Allow",
Resource = [
"*"
],
Action = [
"ec2:*"
]
},
],
length(var.cloudfront_distributionId) != 0 ? [
{
Effect = "Allow",
Action = [
"cloudfront:ListInvalidations",
"cloudfront:GetInvalidation",
"cloudfront:CreateInvalidation"
],
Resource = [
for cf_id in var.cloudfront_distributionId :
"arn:aws:cloudfront::xxxxxxxx:distribution/${cf_id}"
]
}
] : [],
)
}
}

merge lists of dictionaries in terraform v0.12

I would like to do the following using terraform:
I have 2 JSONs:
1.json:
[
{
"description": "description1",
"url": "url1",
"data": "data1"
},
{
"description": "description2",
"url": "url2",
"data": "data2",
"action": "action2"
},
{
"description": "description3",
"url": "url3",
"data": "data3"
}
]
2.json:
[
{
"description": "description1",
"url": "url1",
"data": "data1"
},
{
"description": "description2_new",
"url": "url2",
"data": "data2_new"
},
{
"description": "description4",
"url": "url4",
"data": "data4"
}
]
and I want to merge them into one. Dictionaries from the second JSON should override dictionaries from the first one if url key is the same. I.e. combined JSON should look like:
[
{
"description": "description1",
"url": "url1",
"data": "data1"
},
{
"description": "description2_new",
"url": "url2",
"data": "data2_new"
},
{
"description": "description3",
"url": "url3",
"data": "data3"
},
{
"description": "description4",
"url": "url4",
"data": "data4"
}
]
Using python I can easily do it:
import json
with open('1.json') as f:
json1 = json.load(f)
with open('2.json') as f:
json2 = json.load(f)
def list_to_dict(json_list):
res_dict = {}
for d in json_list:
res_dict[d['url']] = d
return res_dict
def merge_json(json1, json2):
j1 = list_to_dict(json1)
j2 = list_to_dict(json2)
j1.update(j2)
res_list = []
for key in j1.keys():
res_list.append(j1[key])
return res_list
print(json.dumps(merge_json(json1, json2), indent=4))
How can I do that using terraform?
Using terraform 0.12.x
$ cat main.tf
locals {
# read from files and turn into json
list1 = jsondecode(file("1.json"))
list2 = jsondecode(file("2.json"))
# iterate over lists and turn url into a unique key
dict1 = { for item in local.list1 : item.url => item }
dict2 = { for item in local.list2 : item.url => item }
# combine both dictionaries so values converge
# only take its values
merged = values(merge(local.dict1, local.dict2))
}
output "this" {
value = local.merged
}
$ terraform apply
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
Outputs:
this = [
{
"data" = "data1"
"description" = "description1"
"url" = "url1"
},
{
"data" = "data2_new"
"description" = "description2_new"
"url" = "url2"
},
{
"data" = "data3"
"description" = "description3"
"url" = "url3"
},
{
"data" = "data4"
"description" = "description4"
"url" = "url4"
},
]
Terraform supports expanding a list into function parameters using the ... operator. This will allow an arbitrary number of documents to be read.
(I'm not sure, but I believe this feature was added in v0.15)
For this example, I added a new file 3.json with the contents:
[
{
"description": "description4_new",
"url": "url4",
"data": "data4_new"
}
]
For main.tf, I'm using the same logic as #someguyonacomputer's answer:
$ cat main.tf
locals {
jsondocs = [
for filename in fileset(path.module, "*.json") : jsondecode(file(filename))
]
as_dicts = [
for arr in local.jsondocs : {
for obj in arr : obj.url => obj
}
]
# This is where the '...' operator is used
merged = merge(local.as_dicts...)
}
output "as_list" {
value = values(local.merged)
}
Result:
Changes to Outputs:
+ as_list = [
+ {
+ data = "data1"
+ description = "description1"
+ url = "url1"
},
+ {
+ data = "data2_new"
+ description = "description2_new"
+ url = "url2"
},
+ {
+ data = "data3"
+ description = "description3"
+ url = "url3"
},
+ {
+ data = "data4_new"
+ description = "description4_new"
+ url = "url4"
},
]
References:
Terraform Docs -- Function Calls # Expanding Function Arguments