Home > OS >  Terraform for loop in a for loop
Terraform for loop in a for loop

Time:03-13

I have the following Azure AD service principal in my terraform module.

module "test_app" {
  source = "../../../../../my-adapplication/"
  app_owners     = ["[email protected]","[email protected]"]
  app_roles      = [
    {
      allowed_member_types = [
        "User"
      ]
      description = "Read access to Test app"
      display_name = "Test App Read"
      is_enabled   = true
      value        = "TestApp.Read"
      id           = random_uuid.test_app_read.result
    },
    {
      allowed_member_types = [
        "User"
      ]
      description = "Write access to Test app"
      display_name = "Test App Write"
      is_enabled   = true
      value        = "TestApp.Write"
      id           = random_uuid.test_app_write.result
    },
    {
      allowed_member_types = [
        "User"
      ]
      description = "Admin access to Test app"
      display_name = "Test App Admin"
      is_enabled   = true
      value        = "TestApp.Admin"
      id           = random_uuid.test_app_admin.result
    }
  ]

 


  app_role_assignments = [
    {
       app_role_id        = random_uuid.test_app_read.result #"TestApp.Read"
       principal_object_id = data.azuread_group.group_role_read.object_id 
    },
    {
       app_role_id        = random_uuid.test_app_write.result #"TestApp.Write"
       principal_object_id = data.azuread_group.group_role_write.object_id
    },
    {
       app_role_id        = random_uuid.test_app_admin.result #"TestApp.Admin"
       principal_object_id = data.azuread_group.group_role_write.object_id
    }
  ]
   

}

resource "random_uuid" "test_app_read" {
}

resource "random_uuid" "test_app_write" {
}

resource "random_uuid" "test_app_admin" {
}

data "azuread_group" "group_role_read" {
  display_name = "group-role-read"
}

data "azuread_group" "group2_role_read" {
  display_name = "group2-role-read"
}

data "azuread_group" "group_role_write" {
  display_name = "group-role-write"
}

data "azuread_group" "group_role_admin" {
  display_name = "group-role-admin"
}

The my-adapplication module file looks like this:

resource "azuread_application" "app" {
  ...
  ...
  dynamic "app_role" {
    for_each = var.app_roles
    content {
      id                   = app_role.value["id"]
      allowed_member_types = app_role.value["allowed_member_types"]
      description          = app_role.value["description"]
      display_name         = app_role.value["display_name"]
      enabled              = app_role.value["is_enabled"]
      value                = app_role.value["value"]
    }
  }
}

resource "azuread_service_principal" "sp" {
  application_id               = azuread_application.app.application_id
}


resource "azuread_app_role_assignment" "role" {
  for_each            = { for a in var.app_role_assignments : a.app_role_id => a }
  app_role_id         = each.value["app_role_id"]
  principal_object_id = each.value["principal_object_id"]
  resource_object_id  = azuread_service_principal.sp.object_id


}

The issue I am having is related to the app_role_assignments. If I pass in only a single principal_object_id it works. However if I pass in multiple principal_object_ids it doesn't work. For example TestApp.Read below:

app_role_assignments = [
    {
       app_role_id        = random_uuid.test_app_read.result #"TestApp.Read"
       principal_object_id = [data.azuread_group.group_role_read.object_id,data.azuread_group.group2_role_read.object_id]
    },
    {
       app_role_id        = random_uuid.test_app_write.result #"TestApp.Write"
       principal_object_id = data.azuread_group.group_role_write.object_id
    },
    {
       app_role_id        = random_uuid.test_app_admin.result #"TestApp.Admin"
       principal_object_id = data.azuread_group.group_role_write.object_id
    }
  ]

The error received is:

Error: Incorrect attribute value type
│ 
│   on .terraform/modules/test_app/main.tf line 116, in resource "azuread_app_role_assignment" "role":
│  116:   principal_object_id = each.value["principal_object_id"]
│     ├────────────────
│     │ each.value["principal_object_id"] is tuple with 2 elements
│ 
│ Inappropriate value for attribute "principal_object_id": string required.
╵

How do I get terraform to loop over this principal_object_id list? I guess I am after a loop inside a loop. Is there a better way of doing this than the way I am above?

Is it possible to do this using for_each so I don't have the problems with list order changing if i use count/for.

Many thanks in advance.

CodePudding user response:

You have to re-organize your app_role_assignments and then flatten it. If you want principal_object_id to have more then one value, it should always be a list, even for a single element:

app_role_assignments = [
    {
       app_role_id        = random_uuid.test_app_read.result #"TestApp.Read"
       principal_object_id = [data.azuread_group.group_role_read.object_id,data.azuread_group.group2_role_read.object_id]
    },
    {
       app_role_id        = random_uuid.test_app_write.result #"TestApp.Write"
       principal_object_id = [data.azuread_group.group_role_write.object_id]
    },
    {
       app_role_id        = random_uuid.test_app_admin.result #"TestApp.Admin"
       principal_object_id = [data.azuread_group.group_role_write.object_id]
    }
  ]

then you can flatten is as:

locals {
  app_role_assignments_flat = merge([
      for val in var.app_role_assignments: {
        for principal_object_id in val["principal_object_id"]: 
            "${val.app_role_id}-${principal_object_id}" => {
                app_role_id = val.app_role_id
                principal_object_id = principal_object_id
          }
      }
    ]...) # please do NOT remove the dots
}

then

resource "azuread_app_role_assignment" "role" {
  for_each            = local.app_role_assignments_flat
  app_role_id         = each.value["app_role_id"]
  principal_object_id = each.value["principal_object_id"]
  resource_object_id  = azuread_service_principal.sp.object_id
}
  • Related