Is it possible to use variables within the json file example
“security_groups”: “module.nsg.security_groups.id”
When I try, it just passes the string module.nsg.security_groups.id I also tried ${module.nsg.security_groupds.id)
Is it possible to use variables within the json file example
“security_groups”: “module.nsg.security_groups.id”
When I try, it just passes the string module.nsg.security_groups.id I also tried ${module.nsg.security_groupds.id)
Hi @brandootr! You bumped a very old topic and so I’ve moved your question into a separate topic so we can discuss it without creating notification noise for those who participated in the original topic.
I assume you are talking about the JSON variant of the Terraform language, which is to say that you are writing or generating .tf.json
files to define your infrastructure objects.
In any situation where the native syntax would expect a general value expression (which includes all normal resource arguments), the Expression Mapping describes how Terraform translates your JSON input into corresponding concepts from the native syntax.
Notice that the description of how Terraform interprets a string value says:
Parsed as a string template and then evaluated as described below.
The subsequent paragraphs then go on to show an example of referring to another object using template interpolation inside a JSON string:
{
"output": {
"example": {
"value": "${aws_instance.example}"
}
}
}
The above is showing a JSON declaration of an output
block, whose native syntax equivalent would be the following:
output "example" {
value = aws_instance.example
}
From your question it sounds like you already tried something like this and encountered an error. If the above doesn’t answer your question, it would be helpful if you could show the full, exact configuration you tried (the one where you tried ${module.nsg.security_groupds.id
) and the full error message Terraform returned in response to it, and then I can hopefully understand and explain why your particular attempt didn’t work.
My main.tf looks like this
data "template_file" "instance" {
template = "${file("${path.module}/instance.json")}"
}
locals {
instance = data.template_file.instance
}
My instance.json looks like this
{
"UATDB01": {
"AZ": "$${data.oci_identity_availability_domain.AD-1}",
"env_number": "1",
"instance_shape_config_memory_in_gbs": "60",
"subnet_id": "$${data.oci_core_subnets.internal1.subnets[0].id}",
"fault_domain": "FAULT-DOMAIN-1",
"image_id": "$${data.oci_core_images.this.images[0].id}",
"ocpu_count": "4",
"shape": "VM.Standard.E3.Flex",
"public_ip": "false",
"security_groups": "$${module.nsg.security_groups.id}"
},
"UATDB02": {
"AZ": "$${data.oci_identity_availability_domain.AD-1}",
"env_number": "1",
"instance_shape_config_memory_in_gbs": "60",
"subnet_id": "$${data.oci_core_subnets.internal1.subnets[0].id}",
"fault_domain": "FAULT-DOMAIN-1",
"image_id": "$${data.oci_core_images.this.images[0].id}",
"ocpu_count": "4",
"shape": "VM.Standard.E3.Flex",
"public_ip": "false",
"security_groups": "$${module.nsg.security_groups.id}"
}
}
My Module looks like this
module "instance" {
source = “”
for_each = data.template_file.instance
compartment_id = oci_identity_compartment.this.id
fault_domain = each.value.fault_domain
AZ = "${each.value.AZ}"
shape = each.value.shape
security_groups = each.value.security_groups
tags = module.environment.tags
name = each.key
instance_shape_config_memory_in_gbs = each.value.instance_shape_config_memory_in_gbs
user_data = module.cloud_init.windows
subnet_id = each.value.subnet_id
image_id = each.value.image_id
ocpu_count = each.value.ocpu_count
}
When I execute a Terraform plan I get this
168: ocpu_count = each.value.ocpu_count
├────────────────
│ each.value is "{\r\n \"UATDB01\": {\r\n \"AZ\": \"$${data.oci_identity_availability_domain.AD-1}\",\r\n \"env_number\": \"1\",\r\n \"instance_shape_config_memory_in_gbs\": \"60\",\r\n \"subnet_id\": \"$${data.oci_core_subnets.internal1.subnets[0].id}\",\r\n \"fault_domain\": \"FAULT-DOMAIN-1\",\r\n
\"image_id\": \"$${data.oci_core_images.this.images[0].id}\",\r\n \"ocpu_count\": \"4\",\r\n \"shape\": \"VM.Standard.E3.Flex\",\r\n \"public_ip\": \"false\",\r\n \"security_groups\": \"$${module.nsg.security_groups.id}\"\r\n },\r\n \"UATDB02\": {\r\n \"AZ\": \"$${data.oci_identity_availability_domain.AD-1}\",\r\n \"env_number\": \"1\",\r\n \"instance_shape_config_memory_in_gbs\": \"60\",\r\n \"subnet_id\": \"$${data.oci_core_subnets.internal1.subnets[0].id}\",\r\n \"fault_domain\": \"FAULT-DOMAIN-1\",\r\n \"image_id\": \"$${data.oci_core_images.this.images[0].id}\",\r\n \"ocpu_count\": \"4\",\r\n
\"shape\": \"VM.Standard.E3.Flex\",\r\n \"public_ip\": \"false\",\r\n \"security_groups\": \"$${module.nsg.security_groups.id}\"\r\n }\r\n}"
Can't access attributes on a primitive-typed value (string).
Also, I am using Terraform version v1.0.9 for Windows.
Hi @brandootr,
What you have here is not a JSON Configuration File, but rather a Terraform template that happens to generate JSON. That might seem like a pedantic distinction to make, but it’s particularly important here because Terraform has no awareness that this template is generating JSON, and is instead just treating it like any other string template.
The relevant documentation is therefore string template syntax, rather than the JSON Configuration Syntax I linked before.
With that said, I see that you are using the deprecated template_file
data source here, rather than the current templatefile
function. The documentation for the current function includes a section specifically about generating JSON or YAML from a template, which shows our recommended way to do it, which allows you to describe the data structure using Terraform’s expression syntax rather than Terraform’s template syntax, and can therefore avoid annoying escaping, quoting, and delimiter-related problems.
First let’s see what it looks like to remove the deprecated template_file
data source and use the templatefile
function instead. The following configuration replaces what you showed as being in your main.tf
file:
locals {
instance = templatefile("${path.module}/instance.json", {})
}
If you just change the above then I expect you’ll see just a slightly different version of the same error message you saw before, because Terraform is still parsing that file with the same template syntax, but now the parsing is happening inside Terraform itself rather than in the deprecated hashicorp/template
provider.
From here there are two more problems to solve:
templatefile
, which I set to just {}
(an empty object) in the above example.For the first of those, this template seems to need a few different values and so the goal is to pass each of those values into that second argument of templatefile
using names that will make sense in the context of the template. I don’t know enough about your underlying system to know what names to pick here and so I’m going to make a guess here but you should of course feel free to rename and adjust here as needed. Again, this replaces my previous example, which in turn replaced your main.tf
example:
locals {
instance = templatefile("${path.module}/instance.json", {
az = data.oci_identity_availability_domain.AD-1
subnet_id = data.oci_core_subnets.internal1.subnets[0].id
image_id = data.oci_core_images.this.images[0].id
security_group_ids = module.nsg.security_groups.ids
})
}
Notice that because the template namespace is separate from the Terraform module namespace, we can assign shorter meaningful names to each of the values passed in, which is what we’ll then use inside the template.
The instance.json
file would then look something like the following, based on the advice on the templatefile
documentation page about generating JSON:
${jsonencode({
UATDB01 = {
AZ = az
env_number = "1"
instance_shape_config_memory_in_gbs = "60"
subnet_id = subnet_id
fault_domain = "FAULT-DOMAIN-1"
image_id = image_id
ocpu_count = "4"
shape = "VM.Standard.E3.Flex"
public_ip = "false"
security_groups = security_group_ids
}
UATDB02 = {
AZ = az
env_number = "1"
instance_shape_config_memory_in_gbs = "60"
subnet_id = subnet_id
fault_domain = "FAULT-DOMAIN-1"
image_id = image_id
ocpu_count = "4"
shape = "VM.Standard.E3.Flex"
public_ip = "false"
security_groups = security_group_ids
}
})}
Notice that this template is really just one big interpolation sequence ${ ... }
, and so the result of the template is just the result if the expression inside. In this case it immediately calls jsonencode
, and so the result is a JSON string.
The argument to jsonencode
is any valid Terraform expression, whose result Terraform will convert to JSON using the mapping table in the jsonencode
documentation, and so the result should be the same JSON data structure as described in your original example.
This is awesome information. Thank you so much. Let me make some modifications and see if I can get this to work.