Iterate a map in a map - terraform

I have a variable in tf declared as an object map that takes a string, and a child map of string values.
variable "apps" {
type = map(object({
hits = number,
id = map(string),
}))
Variable looks like this:
default = {
test = {
hits = 1,
id = {
one = "image-1234"
two = "image-4567"
}
}
In a resource declaration, I need the hits variable but also the child keys and values of the id map. How can I achieve this?
I have tried many code iterations, but would love to do something like:
name = id[each.key] to get the current key
I tried using count and getting a list of keys/values, e.g
I also tried to see if I can get the key value from the map, e.g.:
description = each.value[currentiteration].Value
In other words, I tried lots of combinations, even though the above is not right.

Related

Terraform: Can I set variable type = any dict/object

Is there a way to allow any type of dictionary/object as an input variable?
I have a module to create cron expressions with lambda and I'm trying to add a variable to take in a dictionary to pass into the resource call.
I'd like just allow any dictionary of any length. With any type for the keys and values in it.
Initially, I tried just:
variable vars {
type = object
}
But that isn't allowed.
Right now I just have the type empty, so it will accept anything, but that doesn't seem like good practice.
Ideally this would be the complex type map(any) to specify it must be a map with any type nested. However, you state that:
I'd like just allow any dictionary of any length. With any type for the keys and values in it.
Unfortunately there is a stipulation that map(any) type for a variable declaration must have a consistent structure across all values for the entries. Therefore, you could use map(any) if the input structure is consistent like:
{
"one" = { "a_key" = "a_value", "another_key" = "another_value" },
"two" = { "a_key" = "value", "another_key" = "the_value" },
}
However an inconsistent structure:
{
"one" = { "another_key" = "another_value" },
"two" = { "a_key" = "value", "another_key" = 0 },
}
would force the any type, which is the least restrictive and what you stated you did not want, but it is your only option in that situation.
You can just it like this
variable vars {
type = any
}

Replace multiple ids in a path after validation [Golang]

I have a path delimited by /and an expectation to have multiple ids I want to replace with a constant value. The issue I am facing is that once it validates the first id, it performs the replacement and stops. My assumption is that I should be having some sort of do while in Golang (see this resource - I know there is no such a construct in Go) and I have attempted at using:
for true {
// do something
}
but still only the first id is replaced. Any idea? Thank you
Here is my Go Playground example with the original implementation
The problem is you early return after first match. Since you iterating parts of path you should assign result of strings.Replace() to a variable and return after for loop. Assign to path instead of returning and it should work as expected.
func SubstituteById(path string, repl string) string {
ids := strings.Split(path, "/")
for _, id := range ids {
if fastuuid.ValidHex128(id) {
path = strings.Replace(path, id, repl, -1)
}
}
return path
}

What is the key in complex data type map(object)

I am new to terraform and I am trying to understand the below code snippet.
Variable is of type map(objects) and Its looping over map(objects) and keys function takes a map and returns a list containing the keys from that map. Example: https://www.terraform.io/docs/configuration/functions/keys.html
I believe the output will be something like:
network_ids = {
network_alias = 123
network_alias = 456
network_alias = 789
}
What will be the value for network_alias? I went through many links but I am unable to understand.
code snippet:
locals {
network_ids = {
for network_alias in keys(var.networks) :
network_alias => aws_vpc.subnet[network_alias].id
}
}
variable "networks" {
type = map(object({
network_number = string
availability_zone = string
}))
}
I'm happy to break this down.
So, to begin with, network_ids is a terraform local value. Generally, local values are used to store off computations that you don't want to repeat over and over again.
network_ids is specifically a map for expression. map for expressions are used to build up maps from other, "enumerable" values.
In terraform a map is like a HashMap in other languages (dict in python, Hash in ruby, HashMap in Java and so on). map contains an association list mapping a unique key (always a string) to a value which could be of any, consistent type (meaning you can't have things like { "a" = 4, "b" = "c" } since 4 and "c" have different types).
In your specific example, for network_alias in keys(var.networks) says, basically
loop through the keys (again, strings) in var.networks and bind each one to the name network_alias.
The network_alias => aws_vpc.subnet[network_alias].id part says
build a new map where the key is the same as the keys we're looping over, and the value is a lookup of a subnet's ID indexed by the key.
That all being said, local.network_ids will not end up looking like the following, because maps have distinct keys and the comprehension actually evaluates the key value
network_ids = {
network_alias = 123
network_alias = 456
network_alias = 789
}
It's impossible for me to tell you exactly what the value will be because I don't know the value of var.networks nor that of aws_vpc.subnet.

terraform: performing a map operation on a list?

I have a terraform list
a = [1,2,3,4]
Is there a way for me to apply a function (e.g. *2) on the list, to get
b = [2,4,6,8]
I was looking for an interpolation syntax, perhaps map(a, _*2), or even something like
variable "b" {
count = "${length(a)}"
value = "${element(a, count.index)} * 2
}
As far as I can see no such thing exists. Am I missing something?
As per #Rowan Jacob's answer, this is now possible in v0.12 using the new for expression.
See: https://www.terraform.io/docs/configuration/expressions.html#for-expressions
variable "a" {
type = "list"
default = [1,2,3,4]
}
locals {
b = [for x in var.a : x * 2]
}
output "local_b" {
value = "${local.b}"
}
gives
Outputs:
local_b = [2, 4, 6, 8,]
This is currently an open issue. A new version of Terraform was recently announced which should give the ability to do this, among many other HCL improvements.
I think currently your best bet would be to create local values for each element of the list (remember that you can't use interpolation syntax in the default value of variables; locals exist to get around this limitation). However, I'm not sure if locals have a count attribute.

Groovy: Plucking values out of a Map and into a Set

Groovy here. I have a class Fizz:
#Canonical
class Fizz {
Integer id
String name
}
In my program, I compose a map of them by their integral id field:
// Here, each key of the map corresponds to the Fizz#id of its value
// example:
// allFizzes[1] = new Fizz(1, 'Foobar')
// allFizzes[3004] = new Fizz(3004, 'Wakka wakka')
Map<Integer,Fizz> allFizzes = getSomehow()
I would know like to obtain a set of "bad" Fizzes whose name equals the string 'Sampson'. My best attempt:
Set<Fizz> badFizzes = allFizzes.find { k,v -> v.equals('Sampson') }
However this gives me runtime errors:
Exception in thread "main" org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object '24={
"id": 24,
"name": "Sampson"
},
' with class 'java.util.LinkedHashMap$Entry' to class 'java.util.Set'
So it looks like even though I'm specifying v.equals('Sampson') that Groovy is strill trying to save a Map key,value pair into a Set. What's the Grooviest solution here?
You'll want to use findAll (which returns a Collection) in place of find (which returns an Object). findAll applies the passed in closure to each EntrySet in the Map and returns a new collection with elements that satisfied your closure's criteria. You can then call values() on this newly returned collection.
Map<String, Integer> myMap = ["fizzStrength":29, "fizzQuality": 123, "fizzFizziness":3]
Set<Integer> found = myMap.findAll { k,v -> v > 4 }.values() // returns [29, 123]

Resources