Terraform Reference Item In For-Each Module - foreach

I want to figure out how I can reference an item and it's attributes created in a Terraform for-each loop.
The current situation is with AWS IAM roles and policy attachments.
aws_iam_role module:
main.tf
resource "aws_iam_role" "iam_role" {
for_each = var.roles
name = each.value.role_name
description = each.value.description
assume_role_policy = file(each.value.assume_role_policy)
max_session_duration = each.value.max_session_duration
}
variables.tf:
variable "roles" {
type = map(object({
role_name = string
description = string
assume_role_policy = string
max_session_duration = string
}))
}
outputs.tf:
output "id" {
value = [
for role in aws_iam_role.iam_role:
role.id
]
}
output "arn" {
value = [
for role in aws_iam_role.iam_role:
role.arn
]
}
output "name" {
value = [
for role in aws_iam_role.iam_role:
role.name
]
}
output "RoleNameARNMapping" {
value = {
for role in aws_iam_role.iam_role:
role.name => role.arn
}
}
aws_policy_attachment module:
main.tf
resource "aws_iam_policy_attachment" "policy_attachment" {
for_each = var.attachments
name = each.value.name
users = each.value.users
roles = each.value.roles
groups = each.value.groups
policy_arn = each.value.policy_arn
}
variables.tf
variable "attachments" {
type = map(object({
name = string
users = list(string)
roles = list(string)
groups = list(string)
policy_arn = string
}))
}
Role Configuration:
module "IAM_Roles" {
source = "../modules/aws_iam_role"
roles = {
"iam_role_for_lambda" = {
role_name = "iam_role_for_lambda"
description = "IAM Role For Lambda"
assume_role_policy = "../dev/roles/IAMRoleForLambda.json"
max_session_duration = 3600
}
}
}
I would need to reference the name of the created roles in the policy attachments configuration like this:
module "lambda_role_attachments" {
source = "../modules/aws_iam_policy_attachment"
attachments = {
"lambda-cloudwatch-access" = {
name = "lambda-cloudwatch-access"
users = null
roles = [module.IAM_Roles['iam_role_for_lambda'].name]
groups = null
policy_arn = "arn:aws:iam::aws:policy/CloudWatchLogsFullAccess"
}
}
}
This, unfortunately, does not work. Terraform does not like the quotes:
Single quotes are not valid. Use double quotes (") to enclose strings.
Expected the start of an expression, but found an invalid expression token.

The error you getting is clear Single quotes (') are not valid. Use double quotes (")
But you if you want to access the name by ID ["iam_role_for_lambda"] you need to change the name output to a map:
output "name" {
value = {
for role in aws_iam_role.iam_role :
role.name => role.name
}
}
then where you consume that output it would be:
module "lambda_role_attachments" {
source = "../modules/aws_iam_policy_attachment"
attachments = {
"lambda-cloudwatch-access" = {
name = "lambda-cloudwatch-access"
users = null
roles = [module.IAM_Roles.name["iam_role_for_lambda"]]
groups = null
policy_arn = "arn:aws:iam::aws:policy/CloudWatchLogsFullAccess"
}
}
}
I have a fully working prototype here:
https://github.com/heldersepu/hs-scripts/tree/master/TerraForm/modules
But if you already know the name maybe it would be simpler to just do:
module "lambda_role_attachments" {
source = "../modules/aws_iam_policy_attachment"
attachments = {
"lambda-cloudwatch-access" = {
name = "lambda-cloudwatch-access"
users = null
roles = ["iam_role_for_lambda"]
groups = null
policy_arn = "arn:aws:iam::aws:policy/CloudWatchLogsFullAccess"
}
}
}

Related

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

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.

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

Handle Tree Format Array in Table View -objective c

I have a Json response with tree structure array. Now the problem is i have to display that array in table view. The array format is like
details = {
id = 002;
name = Rohit;
fid = 123;
"Friends" = (
{
id = 003;
name = "Sham";
fid = 2355;
"Friends" = (
{
id = 252;
name = Ram;
Fid = 8568;
"Friends" = (
{
id = 7545;
name = "Rahul";
Fid = 874;
"Friends" = (
);
},
{
id = 77554;
name = "LCMff";
pid = 45425;
"Friends"= (
);
},
{
id = 4545;
name = peter;
fid = 4548;
"Friends"= (
{
id = 785612;
name = "john";
fid = 45;
"Friends" = (
);
}
);
},
},
Above is the example. Now i have to display Friends list in table view on click of name.Also i have to display the name in header like bread crumb. Help me out to resolve this.

The name does not exist in the current context (in ActionResult with MVC Entity Framework)

In ActionResult "Details(int? id)" of a controller I have (in ActionResult this is inside of an condition: if (User.Identity.IsAuthenticated) { bla, bla):
var TheUser = db.AspNetUsers.Where(u => u.Id == CurrentUser)
.Select(u => new
{
ID = u.Id,
Email = u.Email,
Username = u.UserName,
Surrname = u.Surname,
Name = u.Name,
Role = u.Role,
CreditBalance = u.CreditBalance
}).Single();
var TheJournal = db.CreditJournal.Where(tj => tj.CvID == id && tj.UseBy == CurrentUser)
.Select(tj => new
{
IdJournal = tj.IdJournal,
Operation = tj.Operation,
CvID = tj.CvID,
CreditConsumed = tj.CreditConsumed,
UseDate = tj.UseDate,
UseBy = tj.UseBy
});
var Counter = TheJournal.Count();
When I evaluate values in Debug Mode I have:
TheUser |>>> { ID = "56cc2430-4db5-4799-ad41-fa1d103d1967", Email = "sales#maps4u.ro", Username = "sales#maps4u.ro", Surrname = "Laurentiu", Name = "LAZAR", Role = 3, CreditBalance = 75 } <Anonymous Type>
TheJournal |>>> {System.Data.Entity.Infrastructure.DbQuery<<>f__AnonymousType9<int,string,int?,int?,System.DateTime?,string>>} System.Linq.IQueryable<<>f__AnonymousType9<int,string,int?,int?,System.DateTime?,string>> {System.Data.Entity.Infrastructure.DbQuery<<>f__AnonymousType9<int,string,int?,int?,System.DateTime?,string>>}
Counter |>>> The name 'Counter' does not exist in the current context
What can be wrong in the simple code shown above? (The equivalent SQL statement for TheJornal returns for the given criteria, at least 4 records).
Somehow I think to declare the variables outside the condition, but what type do they have to be? (Anyway the first, TheUser is just ok, the issue starts with second, TheJournal )
Use .ToList() for your TheJournal query. And then use Count to get the Counter -
var Counter = TheJournal.Count

Retrieve table name

Is there a way to retrieve the name of a table? For example, from this table I want extract only the keys name "Mimic", "Molibdeno", "Tamarindo", "Wrenna"
UnitScanDB = {
profiles = {
Mimic = {
...
},
Molibdeno = {
...
},
Tamanrindo = {
...
},
Wrenna = {
...
}
}
}
You can iterate over the inner table using pairs:
for k in pairs(UnitScanDB.profiles) do
-- handle k
end
Using the above loop, you could, for example, copy all of the keys into a new table:
local names = {}
for k in pairs(UnitScanDB.profiles) do
table.insert(names, k)
end
You can simply access those keys by their name, like so:
mimic = UnitScanDB.profiles.Mimic
molibdeno = UnitScanDB.profiles.Molibdeno

Resources