Add a block of code with terraform in JSON file - json

I need to add a conditional when the variable is true, add a block of code in my JSON file but if this variable is false, I need it to do nothing
This is my main.tf
resource "grafana_dashboard" "dashboard_test" {
conficonfig_json = template_file("dashboard.json")
data_source = var.data_source
}
I need add this a block of code in my file JSON
{
"datasource": {
"type": "CloudWatch",
"uid": "${mystring}"
}
}

You should probably switch to using templatefile function [1]. In your example, you would then have:
resource "grafana_dashboard" "dashboard_test" {
config_json = templatefile("dashboard.json", {
mystring = "somevalue"
})
data_source = var.data_source
}
If you do not want to hardcode the value for the mystring variable, you could alternatively use a Terraform variable e.g., mystring = var.mystring. I would also avoid giving just the filename and change the block of code to look like this:
resource "grafana_dashboard" "dashboard_test" {
config_json = templatefile("${path.root}/dashboard.json", {
mystring = var.mystring
})
data_source = var.data_source
}
variable "mystring" {}
More information about using path-based variables is in [2].
[1] https://www.terraform.io/language/functions/templatefile
[2] https://www.terraform.io/language/expressions/references#filesystem-and-workspace-info

Related

How add terraform variables in a JSON file

Hey team I’m having trouble finding in the documentation on how to add terraform variables in a JSON file,
I need inject this variable in this JSON,
In this JSON of this shape but not it works,
I did try with var and locals, I tried it with var and locals, but it does not work, it is by default
You could use templatefile function [1]:
locals {
mystring = "Test"
}
resource "grafana_dashboard" "metrics" {
config_json = templatefile("${path.root}/EC2.json.tpl", {
mystring = local.mystring
})
}
For this to work, you would have to change the JSON to be:
"datasource": {
"type": "CloudWatch"
"uid": "${mystring}"
}
The file with JSON data should also be renamed to EC2.json.tpl.
[1] https://www.terraform.io/language/functions/templatefile

Terraform 0.12 AWS resource containing JSON built from variable

To provision tag policies in an AWS organization, I need to build the JSON content from variables. Management of tag policies, scp, etc. shall be centralized, so changes can be applied everywhere: Renaming, adding, removing tags, etc.
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
}
}
}
provider "aws" {
profile = "default"
region = "us-west-1"
}
The problem at hand I am facing is: How would I build the JSON object?
Example variable/ tag map:
# tag_policies.tf
variable "resource_tags" {
description = "Central resource tags"
type = list( object( {
name = string
tags = map(string)
} ) )
default = [
{
name = "Environment"
tags = {
prod = "crn::env:prod"
lab = "crn::env:lab"
dev = "crn::env:dev"
}
}
]
}
What I have tried so far is to use HCL template tags, but I end up with one , comma too much when iterating through the map of tag names. This works fine for the join() with the sub-map of tag names, but does not workout if I try to wrap the template markup. Why did I try this? Because I ran out of ideas.
# vars.tf
resource "aws_organizations_policy" "root-tag-policy" {
name = "RootTagPolicy"
type = "TAG_POLICY"
content = <<CONTENT
{
"tags": {
%{ for tag in var.resource_tags_env ~}
"${tag.name}": {
"tag_key": {
"##assign": "${tag.name}",
"##operators_allowed_for_child_policies": [ "##none" ]
},
"tag_value": { "##assign": [ "${join( ", ", values( tag.tags ) )}" ] }
},
%{ endfor ~}
}
}
CONTENT
}
The solution actually was quite simple: Iterate of the tags using a for expression and enclose it with curly braces { … } to return an object (=> returns tuples).
Finally jsonencode() cares about converting the HCL key = value syntax to proper JSON.
resource "aws_organizations_policy" "root-tag-policy" {
name = "RootTagPolicy"
type = "TAG_POLICY"
content = jsonencode( [ for key, tag in var.resource_tags: {
"${tag.name}" = {
"tag_key" = {
"##assign" = tag.name,
"##operators_allowed_for_child_policies" = [ "##none" ]
},
"tag_value" = { "##assign" = [ join( ", ", values( tag.tags ) ) ] }
}
} ] )
}
EDIT This still does not work, as I forgot that the whole JSON object needs to get wrapped inside a tags: {}.
kaiser's answer shows a good general approach: build a suitable data structure and then pass it to jsonencode to get a valid JSON string from it.
Here's an example that I think matches what the string template in the original question would've produced:
content = jsonencode({
tags = {
for tag in var.resource_tags_env : tag.name => {
tag_key = {
"##assign" = tag.name
"##operators_allowed_for_child_policies" = ["##none"]
}
tag_value = {
"##assign" = values(tag.tags)
}
}
}
})
I'm not familiar with the aws_organizations_policy resource type so I'm sorry if I got some details wrong here, but hopefully you can adapt the above example to generate the JSON data structure you need.
After reading #martin-atkins answer, I finally understood how the for works for objects and maps. The var before the => arrow actually is part of the resulting object. (This highly confused me as I compared it to other languages arrow functions and arguments.)
The first part of the process is to build a map of maps. The main reason is that I don't want to have a convention of a name key in a map of variables. This might lead to handling of conventions later on, what should be avoided at all costs as it is a possible trap if one does not pay close attention or is aware of it. So the key actually is the name now.
Data Structure
variable "resource_tags" {
description = "Central resource tags"
type = map(
map(string)
)
default = {
Environment = {
common = "grn::env:common"
prod = "grn::env:prod"
stage = "grn::env:stage"
dev = "grn::env:dev"
demo = "grn::env:demo"
lab = "grn::env:lab"
},
Foo = {
bar = "baz"
}
}
}
The content as JSON
After understanding that the key in { "tags": { … } } is just the part before the =>, I could reduce the final resource to the following block.
resource "aws_organizations_policy" "root-tag-policy" {
name = "RootTagPolicy"
description = "Tag policies, assigned to the root org."
type = "TAG_POLICY"
content = jsonencode({
tags = {
for key, tags in var.resource_tags : key => {
tag_key = {
"##assign" = key
"##operators_allowed_for_child_policies" = ["##none"]
}
tag_value = {
"##assign" = values( tags )
}
}
}
})
}
Quick test:
Add the following output statement after the resource block:
output "debug" {
value = aws_organizations_policy.tp_root-tag-policy.content
}
Now apply (or plan or refresh) just this resource. It's faster this way. Then output the built debug from the apply or refresh run.
$ terraform apply -target=aws_organizations_policy.root-tag-policy
…things happening…
$ terraform output debug | json_pp
ProTips:
Pipe the output of the output directly into json_pp or jq so you can read it.
Use jq . if you want validation on top. If you see the output, it means it's valid. Else you should receive 0 as response.

How to convert string to number in terraform template file

I have a terraform template file source.tpl - it's a json and it has to be JSON, because it's produced by python json library. This file has the following entry
[
{
"data": {
"address": "${NETWORK}",
"netmask": "${NETMASK}",
}
}
]
In my tf module, I render this template:
data "template_file" "source" {
template = "${file("${path.module}/source.tpl")}"
vars = {
NETWORK = element(split("/", "${var.cidr}"),0)
NETMASK = tonumber(element(split("/", "${var.cidr}"),1))
}
}
where cidr is a string - something like 10.1.1.0/24
In the rendered output I need NETMASK to be a number and NETWORK to be a string. I.e. it has to be something like:
data = {
address = "10.1.1.0"
netmask = 24
}
But I'm getting:
data = {
address = "10.1.1.0"
netmask = "24"
}
I.e. netmask is a string. How can I get rid of those quotes in terraform? Initial source.tpl should still have those quotes, because if I remove them - it becomes invalid JSON.
I understand the problem here, you're generating the template using a JSON library that cannot produce something like the following since it's invalid JSON, though this is what you want for the template to be
[
{
"data": {
"address": "${NETWORK}",
"netmask": ${NETMASK}
}
}
]
Might I recommend a little bit of preprocessing? For example
template = "${replace(file("${path.module}/source.tpl"), "\"$${NETMASK}\"", "$${NETMASK}")}"

Create Terraform resources out of JSON values

I am looking for a way to generate Terraform code based on JSON values.
Imagine I have a JSON file with the following structure:
{
"settings": [
{
"conf": [
{
"setting": "DeploymentPolicy",
"namespace": "aws:elasticbeanstalk:command",
"value": "AllAtOnce"
},
{
"setting": "BatchSize",
"namespace": "aws:elasticbeanstalk:command",
"value": "30"
},
{
"setting": "BatchSizeType",
"namespace": "aws:elasticbeanstalk:command",
"value": "Percentage"
}
]
}
]
}
What I want to do is the following:
Creating a working Terraform resource based on the JSON file values, e.g. a beanstalk environment like this:
resource "aws_elastic_beanstalk_environment" "app_prod" {
name = "${aws_elastic_beanstalk_application_version.app.name}-prod"
application = aws_elastic_beanstalk_application.app.name
solution_stack_name = data.aws_elastic_beanstalk_solution_stack.latest_linux_java.name
wait_for_ready_timeout = "10m"
version_label = aws_elastic_beanstalk_application_version.app.name
# Elastic beanstalk configuration
setting {
name = "DeploymentPolicy"
namespace = "aws:elasticbeanstalk:command"
value = "AllAtOnce"
}
setting {
name = "BatchSize"
namespace = "aws:elasticbeanstalk:command"
value = "30"
}
...
}
Therefore I have to create the settings block in HCL (Terraform configuration) based on the JSON values.
This means the JSON file above should result in:
setting {
name = "DeploymentPolicy"
namespace = "aws:elasticbeanstalk:command"
value = "AllAtOnce"
}
setting {
name = "BatchSize"
namespace = "aws:elasticbeanstalk:command"
value = "30"
}
setting {
name = "BatchSizeType"
namespace = "aws:elasticbeanstalk:command"
value = "Percentage"
}
As you can see, the structure of JSON and HCL is very similar, but not identical. See e.g. settings, conf, or setting instead of name in the JSON.
A possible approach would be to read the JSON values and store them in an array or a map. But I have no idea how I could generate valid HCL and inject it in the desired part of the resource. Furthermore I tried to use a template but Terraform does not support the looping functionality that I need to iterate over the settings.
To sum up:
Input is a JSON file that must be read
JSON contains settings (besides other information)
The number of settings can differ
Somehow I have to generate a settings block
Somehow I have to inject this settings blok in the resource
Does anyone have an idea how to do that? Any other approaches?
Thanks a lot!
Assuming that your JSON object were in a file called settings.json inside your module directory, you could do something like this:
locals {
environment_settings = jsondecode(file("${path.module}/settings.json")).settings[0].conf[0]
}
resource "aws_elastic_beanstalk_environment" "app_prod" {
name = "${aws_elastic_beanstalk_application_version.app.name}-prod"
application = aws_elastic_beanstalk_application.app.name
solution_stack_name = data.aws_elastic_beanstalk_solution_stack.latest_linux_java.name
wait_for_ready_timeout = "10m"
version_label = aws_elastic_beanstalk_application_version.app.name
dynamic "setting" {
for_each = local.environment_settings
content {
namespace = setting.value.namespace
name = setting.value.setting
value = setting.value.value
}
}
}
This special dynamic block is a sort of macro to create repeated setting blocks, each one correlating with one element of the collection given in for_each.
You can do whatever transformations of the input you need using Terraform's expression language in the locals block to ensure that the local.environment_settings value contains one element for each setting block you will generate, and then in the content nested block tell Terraform how to populate the setting arguments based on those element values.

Pass object to javascript function

I have recently been messing around with jQuery on my website, and I have a fairly limited knowledge of Javascript. I am beginning to like the jQuery ability to pass variables to a jQuery function inside the curly braces, like so:
$(somediv).animate({thisisone: 1, thisistwo: 2}, thisisavar);
What I was wondering is how I can write a Javascript function that I can pass items to inside the curly braces? I know you can write functions like this:
function someName(var1, var2, var3...) {
}
but that doesn't support the braces? I also know that you can add no arguments and do this:
function accident() {
for( var i = 0; i < arguments.length; i++ ) {
alert("This accident was caused by " + arguments[i]);
}
}
accident("me","a car","alcohol","a tree that had no right to be in the path of my driving");
but I also want to pass outside variables instead of just a whole line of strings, if that makes sense?
Basically, I want a function that I can pass variables to, like so:
function myFunction(neededcodehere){
//Some code here...
}
myFunction (var1, {"Option 1", "Option 2", "Option 3"}, anothervar);
The "braces" are making an object literal, i.e. they create an object. It is one argument.
Example:
function someFunc(arg) {
alert(arg.foo);
alert(arg.bar);
}
someFunc({foo: "This", bar: "works!"});
the object can be created beforehand as well:
var someObject = {
foo: "This",
bar: "works!"
};
someFunc(someObject);
I recommend to read the MDN JavaScript Guide - Working with Objects.
function myFunction(arg) {
alert(arg.var1 + ' ' + arg.var2 + ' ' + arg.var3);
}
myFunction ({ var1: "Option 1", var2: "Option 2", var3: "Option 3" });
Answering normajeans' question about setting default value.
Create a defaults object with same properties and merge with the arguments object
If using ES6:
function yourFunction(args){
let defaults = {opt1: true, opt2: 'something'};
let params = {...defaults, ...args}; // right-most object overwrites
console.log(params.opt1);
}
Older Browsers using Object.assign(target, source):
function yourFunction(args){
var defaults = {opt1: true, opt2: 'something'};
var params = Object.assign(defaults, args) // args overwrites as it is source
console.log(params.opt1);
}
when you pass an object within curly braces as an argument to a function with one parameter , you're assigning this object to a variable which is the parameter in this case
My cents.
function readable(args) {
const {opt1, opt2, opt3} = args
console.log(opt1 + opt2 + opt3)
// => "My favorite things"
}
readable({
opt1: "My f"
, opt2: "avori"
, opt3: "te things"
})