length of keys in a map in Terraform - odd output - terraform

Given:
variable "foo" {
type = map
default = {
lorem = "ipsum"
dolor = "sit"
}
}
then, in Terraform console, why does
[for k, v in var.foo : length(k) ]
give:
[
4,
5,
]

That's a really odd result. When I run the exact same thing locally, I get the expected result:
> [for k, v in var.foo : length(k) ]
[
5,
5,
]
Do you have a value set for "foo" in a *.auto.tfvars file somewhere? How about an environment variable called TF_VAR_foo? My best guess is that there is a different variable defined somewhere called "foo" that is overwriting the default value specified in the variable declaration.

Related

unable to use sum in terraform

I'm facing issues while using sum in terraform
my local variable is as follows :
> local.total_output
[
"150",
"150",
"150",
]
> sum(local.total_output)
> Error: Invalid function argument
on <console-input> line 1:
(source code not available)
|----------------
| local.total_output is tuple with 3 elements
Invalid value for "list" parameter: argument must be list, set, or tuple of
number values.
>
Am I doing something wrong here?
As the error says, values in the list should be type of number. You can convert them with the for loop:
sum([for str in local.total_output: tonumber(str)])
Since total_output is a local, probably would be easier just declare it as a list of numbers (no quotes):
locals {
total_output = [150, 150, 150]
}
Your total_output is a list of strings. It should be list of numbers:
[
150,
150,
150,
]

Groovy: Adding elements to a collection

I've the following collection
def a = [
b:[
[
c: "x",
d: ["y","z"]
]
],
b1:[
[
c: "x1",
d: ["y1","z1"]
]
]
]
I want to add a new element "w" to d:["y", "z"] to have this d:["y", "z", "w"]
I've tried a.put(d:"w"), a.add(d:"w") but getting exception
groovy.lang.MissingMethodException: No signature of method: java.util.LinkedHashMap.put() is applicable for argument types: (String) values: [w]
You have take into account all your nesting here. You have a map to
list to map. The main problem is the list now, since your example makes
it not clear, how many list items b could hold. So the solution for
your exact example is:
a.b[0].d << "w"

input dynamic nested list into terraform setproduct() function

Problem:
I have a dynamic nested list that I want to input into the terraform setproduct() function. I can't pass the nested list directly to the setproduct() function nor use a for loop to iterate through the nested list within the setproduct() function (see attempts section). The setproduct() function works only when I explicitly define 2 or more single-level lists arguments (see expected output section).
Input: (the sublists are dynamically created which means the sublist index range varies)
locals {
foo = [
[
{"honda" = "passport"},
{"honda" = "civic"}
],
[
{"toyota" = "prius"}
]
]
}
Expected Output:
bar = [
[
{"honda" = "passport"},
{"toyota" = "prius"}
],
[
{"honda" = "civic"},
{"toyota" = "prius"}
]
]
The expected output can be created using the output block below. Although as stated in the problem section, the local.foo nested list has a dynamic 2nd level index range. So explicitly defining the [0] and [1] indice isn't a viable solution.
output "bar" {
value = setproduct(local.foo[0], local.foo[1])
}
Attempts:
#1
output "bar" {
value = setproduct(local.foo)
}
Output error:
on test.tf line 35, in output "attempts":
35: value = setproduct(local.foo)
|----------------
| local.foo is tuple with 2 elements
Call to function "setproduct" failed: at least two arguments are required.
#2
output "bar" {
value = setproduct(local.foo[*])
}
Output error:
on test.tf line 35, in output "attempts":
35: value = setproduct(local.foo[*])
|----------------
| local.foo is tuple with 2 elements
Call to function "setproduct" failed: at least two arguments are required.
I think you are after Expanding Function Arguments:
output "bar" {
value = setproduct(local.foo...)
}

How to iterate a list up to certain element/index in terraform?

This is simple in other language by using for or do while statement. Being a novice, I still can't figure out to do it in Terraform.
The real case that I need to do is to build a connection string to mongoDB replica-set service. The replication_factor is 3, 5 or 7 which means I need to create a list of 2, 4, or 6 hostnames/addresses.
I come up with the following code sofar:
locals {
uri_list = [
"#${alicloud_mongodb_instance.tyk_mongodb-test.id}1.mongodb.${var.region}.rds.aliyuncs.com:${var.db_port}",
"#${alicloud_mongodb_instance.tyk_mongodb-test.id}2.mongodb.${var.region}.rds.aliyuncs.com:${var.db_port}",
"#${alicloud_mongodb_instance.tyk_mongodb-test.id}3.mongodb.${var.region}.rds.aliyuncs.com:${var.db_port}",
"#${alicloud_mongodb_instance.tyk_mongodb-test.id}4.mongodb.${var.region}.rds.aliyuncs.com:${var.db_port}",
"#${alicloud_mongodb_instance.tyk_mongodb-test.id}5.mongodb.${var.region}.rds.aliyuncs.com:${var.db_port}",
"#${alicloud_mongodb_instance.tyk_mongodb-test.id}6.mongodb.${var.region}.rds.aliyuncs.com:${var.db_port}"
]
uri_list_out = [
for uriname in local.uri_list :
lower(uriname)
if substr(uriname,length("${alicloud_mongodb_instance.tyk_mongodb-test.id}") + 1, 1) < var.mongos_config["${var.environment}"]["replication_factor"]
]
}
What I expect from
output "uri_list_out" {
value = local.uri_list_out
}
is the first two elements of uri_list but instead I got only [1,2] for replication_factor = 3. Seems like if instruction in for also modify the output ???
I appreciate any hints to solve this problem.
Hendro
I believe what you really need is the slice(list, startindex, endindex) function:
uri_list_out = [
for uri in slice(local.uri_list, 0, var.mongos_config[var.environment]["replication_factor"] - 1) :
replace(uri, "/^#/", "") # Remove the leading '#'
]
The docs for the slice function
> slice(["a", "b", "c", "d"], 1, 3)
[
"b",
"c",
]

Using ternary in terraform to choose between 2 different lists

I have two different lists list_a and list_b and I want to choose between them based on a value local.server
I've tried this and it doesn't work
locals {
server = "ubuntu"
list_a = "${list("aaa")}"
list_b = "${list("bbb")}"
chosen = "${local.server == "ubuntu" ? local.list_a : local.list_b}"
}
output "chosen" {
value = "${local.chosen}"
}
Error: Error asking for user input: 1 error(s) occurred:
* local.chosen: local.chosen: At column 3, line 1: conditional operator cannot be used with list values in:
${local.server == "ubuntu" ? local.list_a : local.list_b}
In Terraform v0.11, the conditional operator works only with primitive-typed values (strings, numbers, booleans). You can work around this by constructing a map with the two options and then conditionally choosing a key, as opposed to conditionally choosing a value:
locals {
lists = {
a = "${list("aaa")}"
b = "${list("bbb")}"
}
}
output "chosen" {
value = "${local.lists[local.server == "ubuntu" ? "a" : "b"]}"
}
In the forthcoming (at the time of writing) Terraform v0.12 release, the conditional operator works with values of any type, as long as both the true and false expressions can be converted to the same type during type checking, so your original example would work but can be rewritten using the first-class expression syntax also introduced by v0.12:
locals {
server = "ubuntu"
list_a = ["aaa"]
list_b = ["bbb"]
chosen = local.server == "ubuntu" ? local.list_a : local.list_b
}
output "chosen" {
value = local.chosen
}
Interpolation only seems to work on strings so if we make the ternary expression return a string by joining an often unused character i.e. |
delim = "|"
chosen = "${local.server == "ubuntu" ?
join(local.delim, local.list_a) :
join(local.delim, local.list_b)}"
and then we can change the output to split on that same delimiter
output "chosen" {
value = "${split(local.delim, local.chosen)}"
}
✗ terraform apply
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
Outputs:
chosen = [
aaa
]
There is a nice workaround for those still using terraform < 0.12.x:
You can use split to create a string from a list, evaluate ternary then join to make it a list again.
a = ["list", "values", "for ubuntu"]
b = ["list", "values", "for others"]
new_list = "${split(",", local.server == "ubuntu" ? join(",", local.a) : join(",", local.b))}"

Resources