MrG
MrG

Reputation: 25

How feasible is this with Terraform?

I'm trying to write a Terraform module whose purpose is to add members (users and service principals) to Azure AD groups. Here's my code:

# This is our input which contains the display names of users, SPNs and groups

memberships_service_principals = {
    "spn1" = "group1"
    "spn2" = "group2"
}

memberships_users = {
    "user1" = "group1"
    "user1" = "group2"
    "user2" = "group2"
}

# Here we get each SPN's group object ID

data "azuread_group" "groups_service_principals" {

  for_each = var.memberships_service_principals
  display_name = each.value
}

# Here we get each SPN's object ID

data "azuread_service_principal" "service_principals" {

  for_each = var.memberships_service_principals
  display_name = each.key
}

# Here we get each user's group object ID

data "azuread_group" "groups_users" {

  for_each = var.memberships_users
  display_name = each.value
}

# Here we get each user's object ID

data "azuread_user" "users" {

  for_each = var.memberships_users
  user_principal_name = each.key
}

# Here we construct and merge two maps of groups => object IDs

locals {
  service_principal_object_ids = { for k, v in var.memberships_service_principals : data.azuread_service_principal.service_principals["${k}"].object_id => data.azuread_group.groups_service_principals["${k}"].object_id }
  user_object_ids = { for k, v in var.memberships_users : data.azuread_user.users["${k}"].object_id => data.azuread_group.groups_users["${k}"].object_id }
  member_object_ids = merge(local.service_principal_object_ids, local.user_object_ids)
}

# Here we configure the memberships by passing the group and SPN/user object IDs

resource "azuread_group_member" "group_members" {

  for_each = var.member_object_ids
  group_object_id = each.value
  member_object_id = each.key
}

Unfortunately, the above code doesn't work because a map in Terraform can have only unique keys. This rule gets violated in these cases:

  1. When a user/SPN is specified more than once in order to be member of more than one group
  2. Even if the map gets inverted (so as to have groups as keys instead of users/SPNs), group names and IDs are also not unique

I tried to define my input as follows but I still have no idea how to come up with a map of unique keys which to pass to create the memberships via 'resource "azuread_group_member"':

locals {
  test_input1 = [
    {principal = "user1", groups = ["group1", "group2"]},
    {principal = "user2", groups = ["group2"]}
  ]
  test_input2 = [
    { "user1" = ["group1", "group2"] },
    { "user2" = ["group2"] }
  ]
}

Any help and/or ideas will be greatly appreciated!

Upvotes: 0

Views: 307

Answers (1)

Marcin
Marcin

Reputation: 238081

I don't fully understand your setup, but you could create a map from your variables with unique keys as follows:

variable "memberships_users" {
    default = {
        "user1" = "group1"
        "user1" = "group2"
        "user2" = "group2"
    }
}


locals {
    
    service_users = merge([
          for k1,v1 in var.memberships_service_principals:
              {
                  for k2,v2 in var.memberships_users:
                      "${k1}-${k2}" => {
                          memberships_service_principal = k1
                          memberships_service_principal_group = v1
                          memberships_user = k2
                          memberships_user_group = v2                          
                      }
              }
            ]...)
}

which gives:

service_users = {                                                                                                                                                                                                        
  "spn1-user1" = {                          
    "memberships_service_principal" = "spn1"                                                           
    "memberships_service_principal_group" = "group1"                                                   
    "memberships_user" = "user1"                                                                       
    "memberships_user_group" = "group2"                                                                
  }                                                                                                    
  "spn1-user2" = {                          
    "memberships_service_principal" = "spn1"                                                           
    "memberships_service_principal_group" = "group1"                                                   
    "memberships_user" = "user2"                                                                       
    "memberships_user_group" = "group2"                                                                
  }                                                                                                    
  "spn2-user1" = {                          
    "memberships_service_principal" = "spn2"                                                           
    "memberships_service_principal_group" = "group2"
    "memberships_user" = "user1"                                                                       
    "memberships_user_group" = "group2"                                                                
  }                                                                                                    
  "spn2-user2" = {                          
    "memberships_service_principal" = "spn2"                                                           
    "memberships_service_principal_group" = "group2"
    "memberships_user" = "user2"       
    "memberships_user_group" = "group2"
  }
}    

Then you could use that:

# SAMPLE CODE. Its not clear what you wish to accomplish,
# thus some changes probably will be required!
resource "azuread_group_member" "group_members" {
  for_each = local.service_users
  group_object_id = data.azuread_group.groups_service_principals[each.value.memberships_service_principal].object_id 
  member_object_id =data.azuread_group.groups_users[each.value.memberships_user].object_id
}

Upvotes: 2

Related Questions