Terraform HCL - Convert List to Map of Objects? - terraform

I have a list of strings, that I need to transform to a map so that when I do jsonencode on it later, it doesn't create an Array. This is because in json-schema the properties: { ... } is not a list of properties but actually a map.
So each property in my list should come out as a key - value map. Where the key is the property name and the value is another map or object { "type" = "string" }.
additional-properties = [
for prop in local.prop-list:
{ prop = { "type" = "string" }}
]
My first attempt ends up as a list of map objects, instead of a map of properties.
Any better way to accomplish this?
My ultimate goal is to be able to use jsonencode on this in a json schema for an API Gateway model -
"properties": {
"prop1": {
"type": "string"
},
"prop2": {
"type": "string"
}
}

When you specify the assignment to additional-properties as:
[
for prop in local.prop-list:
{ prop = { "type" = "string" }}
]
we can remove the lambda and variables to see the resulting type from the constructors will be:
[{{}}]
which is a nested Map inside a List.
Since you want a nested Map with a { prop { type = string } } structure, we need to specify the constructors accordingly:
additional-properties = { # outside map with "prop" key and map value
for prop in local.prop-list:
prop => { "type" = "string" } # nested map with "type" key and "string" value
}
Note also the change from = to => for proper lambda iterator map key-value pair assignment syntax.

Related

Trying to deploy naming convention in bicep and fails at the portal

I am going parameter file for each environment dev, prod, stg and calling respective parameter with the ps1 by changing the -templateparameter and the environment value.
I am passing index for env, approle and location from the parameter. Intellisense passes.
Eg for location in the main module passing locationShortName: locationList[locationIndex].locationShortname
Dev Parameter file
"locationIndex": {
"value": 1
}
locationList": {
"value": [
{
"Location": "westus2",
"LocationShortName": "azw2"
},
{
"Location": "eastus",
"LocationShortName": "aze"
},
{
"Location": "westus",
"LocationShortName": "azw"
},
{
"Location": "centralus",
"LocationShortName": "azc"
},
{
"Location": "westus3",
"LocationShortName": "azw3"
}
]
}
I get -
The language expression property array index '1' is out of bounds. Even though it should be picking up 'azw2'for 'westus2' shortname on index 1
Trying to call array and object value from parameter file to main module and pass it to submodule.
At the portal it shows - property array index '1' is out of bounds. Even though it should be picking up 'azw2'for 'westus2' shortname on index 1your text
The language expression property array index '1' is out of bounds.
This issue usually occurs when you try to access an index position or array element that is greater than the array element's length. Moreover, if you want to access locationshort azw2, you would pass array index as 0 because array length is taken from the 0-index position.
I tried below output block to retrieve the array elements with the help of index values by adding a for loop.
output locationList array = [for (location, i) in locationList :{
locationList : <ResourceName>.properties.locationList[i]
}]
After a workaround on this, I implemented a sample script for naming conventions in bicep by referring to SO by #Ansuman Bal and made a few changes and was deployed successfully.
resource exampleResource 'Microsoft.Network/virtualNetworks#2019-11-01' = {
name: ResourceName
location: location
properties: {
}
locationList: locationList
}
output locationList array = [for (location, i) in locationList :{
locationList : <ResourceName>.properties.locationList[i]
}]
Output:

Terraform construct a list from map

I have a terraform map like below.
org_sub_accounts = [
{
"id" = "11111111111"
"type" = "test"
},
{
"id" = "22222222222"
"type" = "prod"
},
{
"id" = "33333333333"
"type" = "prod"
}
]
I want to create a list from this map by including the account id where the type is prod. So the output should be something like the below.
prod_accounts = ["22222222222","33333333333"]
Can someone please help? Was trying to figure this out for some time now.
Assuming that org_sub_accounts is defined as a local (modify answer accordingly if otherwise), then this can be constructed from a for expression lambda inside the list constructor:
[for account in local.org_sub_accounts : account.id if account.type == "prod"]
which returns the value:
[
"22222222222",
"33333333333",
]
which can be assigned to a prod_accounts as desired. Note this assumes your original structure is a list(object) as shown in the question, and therefore always contains the keys id and type.

How to add/update data at nested level in existing Index using Logstash mutate plugin

I have multiple logstash pipelines set up on server that feeds data in Index. Every pipelines adds bunch of fields at the first level of Index along with their nested level.
I already have kpi1 and kpi2 values inside Metrics => data with Metrics being nested array. And I have a requirement to add a new pipeline that will feed the value of kpi3. Here is my filter section in the new pipeline that I created:
filter {
ruby {
code => "
event.set('kpi3', event.get('scoreinvitation'))
"
}
mutate {
# Rename the properties according to the document schema.
rename => {"kpi3" => "[metrics][data][kpi3]"}
}
}
It overwrites the Metrics section ( may be because it is an array??). Here is my mapping :
"metrics" : {
"type" : "nested",
"properties" : {
"data" : {
"properties" : {
"kpi1" : {
....
}
}
}
"name" : {
"type" : "text",
....
}
}
}
How can I keep the existing fields (and values) and still add the new fields inside Metrics => Data ? Any help is appeciated.
The Logstash pipeline looks good, however your mapping doesn't make much sense to me if I'm understanding your requirement correctly.
The metrics property doesn't have to be of type nested. In fact, the metrics property is just a json namespace that contains sub-fields / -objects.
Try the following mapping instead
"metrics": {
"properties": {
"data": {
"properties": {
"kpi1": {
# if you want to assign a value to the kpi1 field, it must have a type
}
}
},
"name": {
"type": "text"
}
}
}

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

how to get child key without going with parent key or with iteration

1.I am having a list which contains multiple Maps that looks like below Map.
Each map contains many keys I want to get value of "name"
{
"question":{
"com.forms.tree":{
"requiredByDefault":true,
"questionDetails":{
"com.forms.Details":{
"preferredFormComponent":"TEXT"
}
},
"locale":{
"language":"en"
},
"formField":{
"name":"CUSTOM_347",
"tag":"input",
"url":"Demo"
}
}
},
"Field":"true"
},{
"question":{
"com.forms.tree":{
"questionDetails":{
"com.forms.Details":{
"preferredFormComponent":"TEXT"
}
},
"locale":{
"language":"en"
},
"formField":{
"name":"CUSTOM_348",
"url":"Demo"
}
}
},
"Field":"true"}
I want to get the value of "name" which falls in every Map but don't want to iterate like question?."com.forms.tree"?.formField?.name.
Is there any other approach in groovy?
So given the json:
def jsonTxt = '''{
"question":{
"com.forms.tree":{
"requiredByDefault":true,
"questionDetails":{
"com.forms.Details":{
"preferredFormComponent":"TEXT"
}
},
"locale":{
"name": "test",
"language":"en"
},
"formField":{
"name":"CUSTOM_347",
"tag":"input",
"url":"Demo"
}
}
},
"Field":"true"
}'''
We can parse it with:
import groovy.json.*
def json = new JsonSlurper().parseText(jsonTxt)
You want to find the "formField" entry in that object, so lets write a recursive finder that will walk through out map of maps looking for the first entry with the given key:
static findFirstByKey(Map map, key) {
map.get(key) ?: map.findResult { k, v -> if(v in Map) findFirstByKey(v, key) }
}
And you can then check it works:
assert findFirstByKey(json, 'formField')?.name == "CUSTOM_347"

Resources