Search code examples
terraformterraform-provider-aws

Terraform two vars on one loop


I am trying to create a policy to give access to an x number of glue databases to a role, because there are more than 40 roles each one having access to different databases is not an option to create the policy one by one.

It is also not possible to separate the permissions by creating two statements one for tables and another one for databases as the role has 10 policies and because some of the roles have access to a lot of databases it reaches the maximum number of characters of the policy.

var.glue_database = ["databaseone", "databasetwo", "databasethree"]

data "aws_iam_policy_document" "policy_glue" {
    statement {
    actions = [
        "glue:Get*",
        "glue:Create*",
        "glue:Update*"
    ]
    effect = "Allow"
    resources = [
    for x in var.glue_databases : [
       formatlist("arn:aws:glue:${var.region}:${var.account}:database/%s", "${x}"), 
       formatlist("arn:aws:glue:${var.region}:${var.account}:table/%s/*", "${x}")
    ]
    ]
}
Error: Incorrect attribute value type var glue_database is a list of strings with 3 elements.

I have also tried to convert it to a map:

locals{
glue_db = formatlist("arn:aws:glue:${var.region}:${var.account}:database/%s", "$var.glue_database}")
glue_table = formatlist("arn:aws:glue:${var.region}:${var.account}:table/%s/*", "$var.glue_database}")

database = [ for value in local.glue_db : { value = value } ]
table = [ for value in local.glue_table : { value = value } ]

glue_var = [ for k, v in zipmap (local.database-value[*], local.table.value[*]): {} ]
}

data "aws_iam_policy_document" "policy_glue" {
statement {
    actions = [
        "glue:Get*",
        "glue:Create*",
        "glue:Update*"
    ]
    effect = "Allow"
    resources = [
    for x in local.glue_var : join(",", x.key, x.value) 
    ]
}

Error: Unsupported attribute Can´t access attributes on a primitive-typed value (string)

What I am trying to get this:

var.glue_database = ["databaseone", "databasetwo", "databasethree"]

data "aws_iam_policy_document" "policy_glue" {
    statement {
    actions = [
        "glue:Get*",
        "glue:Create*",
        "glue:Update*"
    ]
    effect = "Allow"
    resources = [
       "arn:aws:glue:us-east-1:12345678:database/databaseone",
       "arn:aws:glue:us-east-1:12345678:table/databaseone/*",
       "arn:aws:glue:us-east-1:12345678:database/databasetwo",
       "arn:aws:glue:us-east-1:12345678:table/databasetwo/*",
       "arn:aws:glue:us-east-1:12345678:database/databasethree",
       "arn:aws:glue:us-east-1:12345678:table/databasethree/*",
    ]
}

Solution

  • The resources field expects a list of resource ARNs. The code you have shown will produce a list of lists of lists of ARNS, something like

    resources = [
      [
        [
          "...database/db1"
        ]
        [
          "...table/db1"
        ]
      ]
      [
        [
          "...database/db2"
        ]
        [
          "...table/db2"
        ]
      ]
    ]
    

    To produce the list that I think you're trying to make, you'll instead want to use code like

    resources = flatten([
      formatlist("arn:aws:glue:${var.region}:${var.account}:database/%s", var.glue_databases), 
      formatlist("arn:aws:glue:${var.region}:${var.account}:table/%s/*", var.glue_databases)
    ])
    

    formatlist takes a list (as its 2nd parameter) and produces another list, so each of those can just take the glue_databases directly, rather than doing it in a for loop. Then we can flatten those two lists into a single one, and the job is done.