Terraform: Iterate over keys in JSON file - json

Using Terraform I need to loop over some JSON and create some files.
This is the file I'm reading in:
{
"files": {
"file1": {
"a": {
"unusedValue": "val"
}
},
"file2": {
"a": {
"unusedValue": "val"
},
"b": {
"unusedValue": "val"
}
}
}
}
I can't change the format of this file, I need to use to create 3 files:
file1a
file2a
file2b
At the minute, I've got this:
locals {
json = jsondecode(file("files.json"))
files = flatten([ for v in local.json.files:
[ for file, fileLetter in v:
{ "file" = file,
"fileLetter" = fileLetter}
]
])
}
# resource local_file file {
# for_each = { for idx, v in local.files: idx => v }
# content = "Temp content"
# filename = "${path.module}/${each.value.file}-${each.value.fileLetter}"
#}
output myout {
value = local.files
}
But it's giving the wrong output - it's taking the contents of the second object rather than it's name and the first bit isn't using the fileA, fileB, etc.
[
+ {
+ file = "a"
+ fileLetter = {
+ unusedValue = "val"
}
},
+ {
+ file = "a"
+ fileLetter = {
+ unusedValue = "val"
}
},
+ {
+ file = "b"
+ fileLetter = {
+ unusedValue = "val"
}
},
]
It should be:
[
+ {
+ file = "file1"
+ fileLetter = "a"
},
+ {
+ file = "file2",
+ fileLetter = "a"
},
+ {
+ file = "file2",
+ fileLetter = "b"
},
]

I think this should work:
files = flatten([ for file, fileObject in local.json.files:
[ for fileLetter, _ in fileObject:
{ "file" = file,
"fileLetter" = fileLetter}
]
])
With the original for v in local.json.files, you're iterating over only the inner objects, e.g.
{
"a": {
"unusedValue": "val"
}
}
Instead, you want to read both the key and the value from the JSON object in files. This can be done by using two temporary variables, file, fileObject. This is the same syntax you originally used in your file, fileLetter.

Related

Accessing the first object in a tuple using terraform

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",
]

How to flatten json array using typescript and Convert it as object

I am trying to convert a json array as a flat json using typescript.
I have a json as below:
"Fields" : [
{
"FieldName" : "xyz";
"FieldValue" : {
"Contents": {
" Company": "ABC"
}
}
}
]
I have to convert as below:
"xyz" : {
"Contents": {
" Company": "ABC"
}
}
Here Fields should be replaced with "xyz", FieldName and FieldValue should need to be removed.
Help me to achieve this using typescript.
If you really only want to work with that first item in your array just rearrange the data with something the following.
const json = JSON.stringify({
Fields: [
{
FieldName: "xyz",
FieldValue: {
Contents: {
" Company": "ABC"
}
}
}
]
});
const parsedJson = JSON.parse(json);
const expectedOutput = {
[parsedJson.Fields[0].FieldName]: parsedJson.Fields[0].FieldValue
};
console.log(expectedOutput);

Replace the contents in multiple JSON files based on their file name?

How to replace the contents in multiple JSON files based on their file name?
I have a certain number of description files and I need to sort their contents by file name.
For ex. files numbered 1.json, 2.json, ... 10.json
All files inside have the same object names, but different numbers
{
"name": "Garlic 8",
"position": 8,
"description": "Only for stuff.",
"external": "row five",
"image": "buffer/8.png",
"basic": [
{
"type": "row",
"value": "one"
},
{
"color": "pure",
"value": "no"
}
]
}
How to replace numbers in all files with numbers from file names?
(i.e. the numbers inside the file must match the file number)
const basePath = process.cwd();
const fs = require("fs");
const {baseUri, namePrefix,} = require(`${basePath}/src/config.js`);
for (
let i = 1;
i <= 10;
i++
)
{
let rawdata = fs.readFileSync(`${basePath}/rename/json/${i}.json`);
let data = JSON.parse(rawdata);
var originalMsg = JSON.stringify(data);
data.each(function(item) {
{
item.name = `${namePrefix} #${i}`;
item.position = `${i}`;
item.image = `${baseUri}/${i}.png`;
}
});
console.log(originalMsg)
console.log(data)
But typeError: data.each is not a function

Convert file paths into JSON structure using bash / jq

Can we convert the below example using jq for bash (https://stedolan.github.io/jq/)?
The requirement is to convert the file paths into json as given in the below example
const data = [
"/parent/child1/grandchild1"
"/parent/child1/grandchild2"
"/parent/child2/grandchild1"
];
const output = {};
let current;
for (const path of data) {
current = output;
for (const segment of path.split('/')) {
if (segment !== '') {
if (!(segment in current)) {
current[segment] = {};
}
current = current[segment];
}
}
}
console.log(output);
The following assumes:
the input is a valid JSON array of "/"-style pathnames of files;
pathnames are all absolute (i.e., begin with "/").
reduce .[] as $entry ({};
($entry | split("/") ) as $names
| $names[1:-1] as $p
| setpath($p; getpath($p) + [$names[-1]]) )
Example
Input
[
"/parent/child1/grandchild1",
"/parent/child1/grandchild2",
"/parent/child2/grandchild3",
"/parent/child2/grandchild4",
"/parent2/child2/grandchild5"
]
Output
{
"parent": {
"child1": [
"grandchild1",
"grandchild2"
],
"child2": [
"grandchild3",
"grandchild4"
]
},
"parent2": {
"child2": [
"grandchild5"
]
}
}

Overwrite a nested list element using jsonnet

I have the following json
{
"namespace": "monitoring",
"name": "alok",
"spec": {
"replicas": 1,
"template": {
"metadata": "aaa",
"spec": {
"containers": [
{
"image": "practodev/test:test",
"env": [
{
"name":"GF_SERVER_HTTP_PORT",
"value":"3000"
},
{
"name":"GF_SERVER_HTTPS_PORT",
"value":"443"
},
]
}
]
}
}
}
}
How do I add deployment_env.json using jsonnet?
{
"env": [
{
"name":"GF_AUTH_DISABLE_LOGIN_FORM",
"value":"false"
},
{
"name":"GF_AUTH_BASIC_ENABLED",
"value":"false"
},
]
}
I need to add it under spec.template.containers[0].env = deployment_env.json
I wrote the below jsonnet to do that. It appends a new element. But i need to change the existing 0th container element in the json. Please suggest how to do it.
local grafana_envs = (import 'custom_grafana/deployment_env.json');
local grafanaDeployment = (import 'nested.json') + {
spec+: {
template+: {
spec+: {
containers+: [{
envs: grafana_envs.env,
}]
}
}
},
};
grafanaDeployment
See below for an implementation that allows adding env to an existing container by its index in the containers[] array.
Do note that jsonnet is much better suited to work with objects (i.e. dictionaries / maps) rather than arrays, thus it needs contrived handling via std.mapWithIndex(), to be able to modify an entry from its matching index.
local grafana_envs = (import 'deployment_env.json');
// Add extra_env to a container by its idx passed containers array
local override_env(containers, idx, extra_env) = (
local f(i, x) = (
if i == idx then x {env+: extra_env} else x
);
std.mapWithIndex(f, containers)
);
local grafanaDeployment = (import 'nested.json') + {
spec+: {
template+: {
spec+: {
containers: override_env(super.containers, 0, grafana_envs.env)
}
}
},
};
grafanaDeployment
Alternative implementation, not relying on the array index position, but image value instead (which makes more sense here as the env must be understood by the image implementation)
local grafana_envs = (import 'deployment_env.json');
local TARGET_CONTAINER_IMAGE = 'practodev/test:test';
local grafanaDeployment = (import 'nested.json') + {
spec+: {
template+: {
spec+: {
containers: [
// TARGET_CONTAINER_IMAGE identifies which container to modify
if x.image == TARGET_CONTAINER_IMAGE
then x { env+: grafana_envs.env }
else x
for x in super.containers
],
},
},
},
};
grafanaDeployment
An alternative to std.mapWithIndex is to explicitly iterate through the indexes based on the size of the list.
local grafana_envs = (import 'deployment_env.json');
local grafanaDeployment = (import 'nested.json') + {
spec+: {
template+: {
spec+: {
containers:
[super.containers[0] { env+: grafana_envs.env }]
+
[
super.containers[i]
for i in std.range(1, std.length(super.containers) - 1)
]
}
}
},
};
grafanaDeployment
If one needed to modify a specific index other than 0, say 5, then they could do so by putting an if i == 5 in the loop.