In my base module I have this:
dynamic "settings" {
for_each = var.settings
content {
type = settings.value["type"]
operator = settings.value["operator"]
property = settings.value["property"]
target = settings.value["target"]
path = settings.value["path"]
}
}
path is a conditional field sometimes present sometimes not and is actually a block. In the calling module:
settings = [
{ "operator" = "lessThan", "type" = "responseTime", "target" = 10000 },
{ "operator" = "is", "type" = "statusCode", "target" = 200 },
{ "operator" = "is", "property" = "allow", "type" = "header", "target" = "true" },
{ "operator" = "validate", "type" = "body", "path" = { "operator" = "contains", "targetValue" = "9754389", "jsonPath" = "141234" } }
]
Now when I try to plan this I get following error:
An argument named "path" is not expected here. Did you mean to define a block of type "path"?
Question is, how can I define a block field inside a dynamic block?
I tried using a nested dynamic block and working with for-each but path can only have one instance, so it gives error that can't have more than one instance.
I haven't created a block inside a dynamic block before, so I'm just guessing here. Have you tried something like this:
dynamic "settings" {
for_each = var.settings
content {
type = settings.value["type"]
operator = settings.value["operator"]
property = settings.value["property"]
target = settings.value["target"]
path {
operator = settings.value["path"]["operator"]
targetValue = settings.value["path"]["targetValue"]
jsonPath = settings.value["path"]["jsonPath"]
}
}
}
As written, that will require that "path" exists in all of your settings entries, but if that works you might be able to fiddle around with some other approach to make it optional.
Just a next-step suggestion, I'm interested to know how you get on.
Related
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.
I am quite new to terraform. I have a provider that will accept JSON as input.
I have stored JSON config files in my project folder, eg:
{
"id": 58187729,
"name": "My dashboard",
"tags": ["mytag1", "mytag2"]
}
and load them into the resource with the below code:
resource "datadog_monitor_json" "monitor_json" {
for_each = fileset(path.module, "/monitors/*.json")
monitor = file("${path.module}/${each.key}")
}
Is there anyway I can easily append to the "tags" key or will the entire JSON need parsing somehow?
Thanks.
Here is an example on how to add extra tags:
locals {
example = jsondecode(file("${path.module}/myfile.json"))
with_extra_tags = merge(local.example,
{tags = concat(
local.example["tags"],["mytag4", "mytag3"])})
}
gives:
test = {
"id" = 58187729
"name" = "My dashboard"
"tags" = [
"mytag1",
"mytag2",
"mytag4",
"mytag3",
]
}
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)
}
Is it possible with HCL to have nested iterations returning a flat list(map) without resorting to flatten?
I have this:
locals {
mappings = flatten([
for record_type in var.record_types : [
for host in var.hosts : {
type = record_type,
host = host
}
]
])
}
I would like to remove the need for flatten like this:
locals {
mappings = [
for record_type in var.record_types :
for host in var.hosts : {
type = record_type,
host = host
}
]
}
But it seems like each for .. in must return data.
One alternative I could think of to only have a single for-loop is using setproduct():
variable "record_types" {
default = ["type1", "type2"]
}
variable "hosts" {
default = ["host1", "host2"]
}
locals {
mappings = [
for i in setproduct(var.record_types, var.hosts) : {
type = i[0],
host = i[1],
}
]
}
output "mappings" {
value = local.mappings
}
after terraform apply resulting in:
Outputs:
mappings = [
{
"host" = "host1"
"type" = "type1"
},
{
"host" = "host2"
"type" = "type1"
},
{
"host" = "host1"
"type" = "type2"
},
{
"host" = "host2"
"type" = "type2"
},
]
Of course, the two variables need to be independent sets here.
If you want to support duplicates or have dependent inputs, flatten() with two loops is the way.
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.