How to call a value in map element only when it matches another var - foreach

I am using the Terraform provider mrparkers/keycloak to attempt to assign Keycloak groups a list of users.
The snippet below creates realms, groups, and users correctly, but I am stumped on the final line for calling a list of users which should belong to the group being looped through.
Anything to point me in the right direction would be hugely appreciated. :)
vars
variable "realms" {
description = "realms"
type = set(string)
default = ["mrpc"]
}
variable "mrpc-groups" {
type = map(object({
name = string
realm = string
members = set(string)
}))
default = {
"0" = {
realm = "mrpc"
name = "mrpc-admins"
members = ["hellfire", "hellfire2"]
},
"1" = {
realm = "mrpc"
name = "mrpc-mods"
members = ["hellfire2"]
}
}
}
variable "mrpc-users" {
type = map(object({
username = string
email = string
first_name = string
last_name = string
realm = string
}))
default = {
"0" = {
realm = "mrpc"
username = "hellfire"
email = "bla#bla.bla"
first_name = "hell"
last_name = "fire"
}
"1" = {
realm = "mrpc"
username = "hellfire2"
email = "bla2#bla.bla"
first_name = "hell2"
last_name = "fire2"
}
}
}
resources
resource "keycloak_realm" "realm" {
for_each = var.realms
realm = each.value
}
resource "keycloak_group" "group" {
for_each = var.mrpc-groups
realm_id = each.value["realm"]
name = each.value["name"]
depends_on = [keycloak_realm.realm]
}
resource "keycloak_user" "user" {
for_each = var.mrpc-users
realm_id = each.value["realm"]
username = each.value["username"]
email = each.value["email"]
first_name = each.value["first_name"]
last_name = each.value["last_name"]
}
resource "keycloak_group_memberships" "group_members" {
for_each = keycloak_group.group
realm_id = each.value["realm_id"]
group_id = each.value["name"]
members = [ "hellfire2" ]
# i want this to be var.mrpc-groups.*.members (* used incorrectly here i think)
# if
# var.mrpc-groups.*.name == each.value["name"]
#
# so that the correct member list in the vars is used when the matching group is being looped over
# any method to get the final outcome is good :)
}

We can use the distinct and flatten functions in conjunction with a for expression within a list constructor to solve this:
distinct(flatten([for key, attrs in var.mrpc_groups : attrs.members]))
As tested locally, this will return the following for your values exactly as requested in the question indicated by var.mrpc-groups.*.members:
members = [
"hellfire",
"hellfire2",
]
The for expression iterates through the variable mrpc_groups map and returns the list(string) value assigned to the members key within each group's key value pairs. The lambda/closure scope variables are simply key and attrs because the context is unclear to me, so I was unsure what a more descriptive name would be.
The returned structure would be a list where each element would be the list assigned to the members key (i.e. [["hellfire", "hellfire2"], ["hellfire2"]]). We use flatten to flatten the list of lists into a single list comprised of the elements of each nested list.
There would still be duplicates in this flattened list, and therefore we use the distinct function to return a list comprised of only unique elements.
For the additional question about assigning the members associated with the group at the current iteration, we can simply implement the following:
members = flatten([for key, attrs in var.mrpc_groups : attrs.members if attrs.name == each.value["name"]])
This will similarly iterate through the map variable of var.mrpc_groups, and construct a list of the members list filtered to only the group matching the name of the current group iterated through keycloak_group.group. We then flatten again because it is also a nested list similar to the first question and answer.
Note that for this additional question it would be easier for you in general and for this answer if you restructured the variable keys to be the name of the group instead of as a nested key value pair.

Related

Iterating Two Maps Inside A Resource using for_each

I'm Trying to create azure SRV record based on some condition, for this multiple target is needed for a single SRV record. If I iterate inside dynamic each.key and each.value is referring the key value outside the dynamic block. is there a way to iterate this record block for required number of time. Also the count is not supported in record block. Could there be any other ways to achieve this without loop also fine.
NOTE: this is a pseudo code for reference.
resource "azurerm_dns_srv_record" "srv_dns_record" {
for_each = { for key, value in var.clusters : key => value if some condition }
name = name
zone_name = azurerm_dns_zone.cluster_dns_zone[each.key].name
resource_group_name = azurerm_resource_group.main.name
ttl = 60
dynamic "record" {
for_each = var.targets
content {
priority = 1
weight = 5
port = 443
target = each.key
}
}
Thanks!
from the docs
The iterator argument (optional) sets the name of a temporary variable that represents the current element of the complex value. If omitted, the name of the variable defaults to the label of the dynamic block ("setting" in the example above).
so to refer to the value of the iterated field in the dynamic block you should use the label of the block not the each word
resource "azurerm_dns_srv_record" "srv_dns_record" {
for_each = { for key, value in var.clusters : key => value if some condition }
name = name
zone_name = azurerm_dns_zone.cluster_dns_zone[each.key].name
resource_group_name = azurerm_resource_group.main.name
ttl = 60
dynamic "record" {
for_each = var.targets
content {
priority = 1
weight = 5
port = 443
target = record.key
}
}
If you want to iterate over var.targets in side a dynamic block, it should be:
target = record.key

Terraform Azure provider - How do I add via terraform more than one key and/or secret for my key vault?

All the examples I saw provide 1 key and or q secret.
Is there a way to add another one (or more)?
To add multiple keys or secrets for your key vault, you just need to add the resources azurerm_key_vault_key and azurerm_key_vault_secret multiple times.
It's recommended to create such resources in the loop. Terraform offers several different looping constructs, each intended to be used in a slightly different scenario:
count parameter: loop over resources.
for_each expressions: loop over resources and inline blocks within a resource.
for expressions: loop over lists and maps.
For example, create one or more keys and secrets with count parameters.
variable "key_lists" {
type = list(string)
default = ["key1","key2","key3"]
}
variable "secret_maps" {
type = map(string)
default = {
"name1"= "value1"
"aaa" = "111"
"bbb" = "222"
}
}
resource "azurerm_key_vault_key" "generated" {
count = length(var.key_lists)
name = var.key_lists[count.index]
key_vault_id = azurerm_key_vault.example.id
key_type = "RSA"
key_size = 2048
key_opts = [
"decrypt",
"encrypt",
"sign",
"unwrapKey",
"verify",
"wrapKey",
]
}
resource "azurerm_key_vault_secret" "example" {
count = length(var.secret_maps)
name = keys(var.secret_maps)[count.index]
value = values(var.secret_maps)[count.index]
key_vault_id = azurerm_key_vault.example.id
}
You could read this blog for more Terraform loop tips.

Using Terraform how can I create a user for each database as well as for each namespace?

My Terraform script currently creates 2 databases for a set of namespaces (1 or more). I now need to create a user for each respective database, and I am having trouble figuring out the correct method to do this.
This is what I have currently...
variable "namespaces" {
type = set(string)
}
variable "databases" {
type = set("server", "analyzer")
}
resource "postgresql_database" "server_databases" {
for_each = toset(var.namespaces)
name = "server_${each.key}"
}
resource "postgresql_database" "analyzer_databases" {
for_each = toset(var.namespaces)
name = "analyzer_${each.key}"
}
resource "random_password" "postgres_password" {
length = 12
}
resource "postgresql_role" "read_only_user" {
name = "readonlyuser"
login = true
password = random_password.postgres_password.result
skip_reassign_owned = true
}
resource "postgresql_grant" "readonly_tables" {
depends_on = [postgresql_database.server_databases, postgresql_database.analyzer_databases]
for_each = toset(var.namespaces)
database = "server_${each.key}"
object_type = "table"
privileges = ["SELECT"]
role = "readonlyuser"
schema = "public"
}
The problem here is database = "server_${each.key}" will only create a user for my server database in each namespace. I am pretty sure I need a nested for_each but I am not sure how to achieve this.
I think it should even be possible to loop over the postgresql_database resources instead of having 2 separate resource's defined

Cannot cast GraphObject to Node

I have a defined AbstractQuery as a Child of GraphObject :
class AbstractQuery(GraphObject):
__primarykey__ = "hash"
hash = Property()
projname = Property()
def __init__(self, hash):
self.hash = hash
self.projname = "" # TODO:initialize this
and iterate over all SQLQuery objects in my (already existing) graph and want to create a new AbstractQuery consisting of a hash. Any SQLQuery hashing to the hash determining the AbstractQuery should be connected:
def addAbstractionLayerSqlQueries(graph, logger=None):
abstractQueryTable = getAbstractQueries(graph, logger)
sqlqueries = graph.data("MATCH (n:SQLQuery) return n")
if logger is not None:
logger.info("abstracting {} queries".format(len(sqlqueries)))
counter = 1
for iterator in sqlqueries:
query = iterator['n']
if logger is not None:
logger.info("abstracting query {}/{}".format(counter,
len(sqlqueries)))
hash = sqlNormalizer.generate_normalized_query_hash(query['ts'])
if hash not in abstractQueryTable:
abstractQueryNode = al.AbstractQuery(hash)
abstractQueryTable[hash] = abstractQueryNode
graph.push(abstractQueryNode)
rel = Relationship(query, "ABSTRACTSTO", abstractQueryTable[hash])
graph.create(rel)
counter = counter + 1
Before I start this process I extract a table (using the hash as key) of all already existing AbstractQueries to prevent creating the same AbstractQuery twice.
However, when I run the method I end up with the exception:
TypeError: Cannot cast AbstractQuery to Node
Why is this and how can I fix this?
I previously entered multiple SQLQueries into my graph by using this representation:
class SQLQuery(GraphObject):
__primarykey__ = "uuid"
uuid = Property()
projname = Property()
session = Property()
user = Property()
seq = Property()
ts = Property()
sql = Property()
Statement = RelatedTo("SQLStatement")
AbstractsTo = RelatedTo("AbstractQuery")
def __init__(self, projname=None, session=None, user=None,
seq=None, ts=None, sql=None):
self.projname = projname
self.session = session
self.user = user
self.seq = seq
self.ts = ts
self.sql = sql
self.uuid = "{} [{} {}] {}.{}.{}".format(type(self).__name__,
seq, ts, projname,
session, user)
As I was able to use this representation to represent and enter Nodes I am quite flabbergasted on why py2neo rejects my AbstractQuery class as a node in my addAbstractionLayerSqlQueries function.
You're mixing two layers of API. The OGM layer sits above the regular py2neo API and a GraphObject does not directly correspond to a Node (it contains other stuff too). Therefore, you cannot build a Relationship to or from a GraphObject directly.
To access the core node behind your GraphObject, you can use my_object.__ogm__.node.
I fixed the problem by only using the Object Model and replacing the graph.data(...) query part:
def addAbstractionLayerSqlQueries(graph, logger=None):
abstractQueryTable = getAbstractQueries(graph, logger)
sqlqueries = list(adlsql.SQLQuery.select(graph))
print type(sqlqueries)
if logger is not None:
logger.info("abstracting {} queries".format(len(sqlqueries)))
counter = 1
for query in sqlqueries:
print type(query)
if logger is not None:
logger.info("abstracting query {}/{}".format(counter,
len(sqlqueries)))
hash = sqlNormalizer.generate_normalized_query_hash(query.ts)
if hash not in abstractQueryTable:
abstractQueryNode = al.AbstractQuery(hash)
abstractQueryTable[hash] = abstractQueryNode
graph.push(abstractQueryNode)
query.AbstractsTo.add(abstractQueryTable[hash])
graph.push(query)
counter = counter + 1
However, the actual reason for the error is still unknown and I'll accept any answer that explains this. This answer is only fixing the problem.

Give one variable a number value and string value?

I am trying to have one variable that has a number value as well as a string value.
I am coding in Lua and I don't know how to do this. Is it possible?
Tables. They are like a filing cabinet where you can store as many values as you want and retrieve them given some kind of "key". In Lua, the key can be any type, but the most common key is going to be a numerical index or a string.
Given:
local age = 30 -- your number values
local name = 'Fred' -- your string value
There's a tons of different ways we can structure that in Lua:
local person = { age = 30, name = 'Fred' )
print(person.age, person.name)
local person = { 'Fred', 30 }
print(person[1], person[2])
print(unpack(person))
local person = { Fred = 30 }
print(person.Fred)
local person = { [30] = 'Fred' }
print(person[30])
So on and so forth.
So if i use..
coal = { name = "Coal", value = 80 }
I can then do this?
userInput = read()
if userInput == coal.name then
fuelUse = coal.value
end

Resources