1
votes

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.

1

1 Answers

1
votes

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"
    }
  }
}