From c9452865b230279e270ff6528c86e67922395a05 Mon Sep 17 00:00:00 2001 From: Lyudmil Ilchev Date: Thu, 4 Dec 2025 16:57:58 +0200 Subject: [PATCH 1/6] add fix for role assignment --- .../role_assignments/built_in_role/_locals.tf | 34 ++++++++++++++ .../role_assignments/built_in_role/main.tf | 46 ++++++++----------- 2 files changed, 54 insertions(+), 26 deletions(-) create mode 100644 src/modules/role_assignments/built_in_role/_locals.tf diff --git a/src/modules/role_assignments/built_in_role/_locals.tf b/src/modules/role_assignments/built_in_role/_locals.tf new file mode 100644 index 00000000..0e5daa96 --- /dev/null +++ b/src/modules/role_assignments/built_in_role/_locals.tf @@ -0,0 +1,34 @@ +locals { + flat_assignments = flatten([ + for role_definition_name, resources in var.settings : [ + for resource_key, resource_details in resources : [ + for principal_type, principals in try(resource_details, {}) : [ + for principal in( + can(principals) && length(principals) > 0 ? principals : [] + ) : { + role_definition_name = role_definition_name + resource_key = resource_key + resource_type = var.resource_type + principal_type = principal_type + principal = principal + } + ] + ] + ] + ]) +} + +locals { + users_email = { + for a in local.flat_assignments : + a.principal => a.principal + if a.principal_type == "users_email" + && !can(regex("^[0-9a-fA-F-]{36}$", a.principal)) + } + + group_names = { + for a in local.flat_assignments : + a.principal => a.principal + if a.principal_type == "group_name" + } +} diff --git a/src/modules/role_assignments/built_in_role/main.tf b/src/modules/role_assignments/built_in_role/main.tf index fc97bf32..79888af5 100644 --- a/src/modules/role_assignments/built_in_role/main.tf +++ b/src/modules/role_assignments/built_in_role/main.tf @@ -1,43 +1,37 @@ resource "azurerm_role_assignment" "main" { for_each = tomap({ - for item in flatten([ - for role_definition_name, resources in var.settings : [ - for resource_key, resource_details in resources : [ - for principal_type, principals in try(resource_details, {}) : [ - for principal in( - # Handle cases where the principal is a list (like object_ids) or a single value - can(principals) && length(principals) > 0 ? principals : [] - ) : { - role_definition_name = role_definition_name - resource_key = resource_key - resource_type = var.resource_type - principal_type = principal_type - principal = principal - } - ] - ] - ] - ]) : - # Ensure unique keys for each role assignment + for item in local.flat_assignments : "${item.role_definition_name}-${item.resource_key}-${item.principal_type}-${item.principal}" => item }) - scope = try(var.resources[ - try(var.settings.lz_key, var.client_config.landingzone_key) - ].virtual_networks[ - split("/", each.value.resource_key)[0] - ].subnets[ - split("/", each.value.resource_key)[1] + scope = try( + var.resources[ + try(var.settings.lz_key, var.client_config.landingzone_key) + ].virtual_networks[ + split("/", each.value.resource_key)[0] + ].subnets[ + split("/", each.value.resource_key)[1] ].id, var.resources[ try(var.settings.lz_key, var.client_config.landingzone_key) ][var.resource_type][each.value.resource_key].id, null ) + principal_id = try( - # If principal is directly an ID (like object_ids), use it. Otherwise, resolve via var.resources. + # 1) Existing: direct object IDs each.value.principal_type == "object_ids" ? each.value.principal + + # 2) NEW: users resolved from email/UPN + : each.value.principal_type == "users_email" + ? data.azuread_user.users[each.value.principal].id + + # 3) NEW: groups resolved from display_name + : each.value.principal_type == "group_name" + ? data.azuread_group.groups[each.value.principal].id + + # 4) Existing: resolve from var.resources (managed identities, etc.) : var.resources[ try(var.settings.lz_key, var.client_config.landingzone_key) ][each.value.principal_type][each.value.principal].principal_id, From 24400381acacfa998686bd0fbceee0edadec27cd Mon Sep 17 00:00:00 2001 From: Lyudmil Ilchev Date: Thu, 4 Dec 2025 17:07:57 +0200 Subject: [PATCH 2/6] fix azuread groups --- src/modules/role_assignments/built_in_role/main.tf | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/modules/role_assignments/built_in_role/main.tf b/src/modules/role_assignments/built_in_role/main.tf index 79888af5..9484a9c4 100644 --- a/src/modules/role_assignments/built_in_role/main.tf +++ b/src/modules/role_assignments/built_in_role/main.tf @@ -40,3 +40,13 @@ resource "azurerm_role_assignment" "main" { role_definition_name = each.value.role_definition_name } + +data "azuread_user" "users" { + for_each = local.users_email + user_principal_name = each.value +} + +data "azuread_group" "groups" { + for_each = local.group_names + display_name = each.value +} From 3e82242e2b7b5527ddf2d041e112166053c349b8 Mon Sep 17 00:00:00 2001 From: Lyudmil Ilchev Date: Thu, 4 Dec 2025 17:21:34 +0200 Subject: [PATCH 3/6] fix azuread groups --- src/modules/role_assignments/built_in_role/_locals.tf | 5 +++-- src/modules/role_assignments/built_in_role/main.tf | 7 ++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/modules/role_assignments/built_in_role/_locals.tf b/src/modules/role_assignments/built_in_role/_locals.tf index 0e5daa96..5b1e3a90 100644 --- a/src/modules/role_assignments/built_in_role/_locals.tf +++ b/src/modules/role_assignments/built_in_role/_locals.tf @@ -1,4 +1,5 @@ locals { + # Exactly your original flatten logic – NO var.resources, NO filtering flat_assignments = flatten([ for role_definition_name, resources in var.settings : [ for resource_key, resource_details in resources : [ @@ -16,9 +17,8 @@ locals { ] ] ]) -} -locals { + # Distinct user emails (UPNs) users_email = { for a in local.flat_assignments : a.principal => a.principal @@ -26,6 +26,7 @@ locals { && !can(regex("^[0-9a-fA-F-]{36}$", a.principal)) } + # Distinct group display names group_names = { for a in local.flat_assignments : a.principal => a.principal diff --git a/src/modules/role_assignments/built_in_role/main.tf b/src/modules/role_assignments/built_in_role/main.tf index 9484a9c4..76bbad31 100644 --- a/src/modules/role_assignments/built_in_role/main.tf +++ b/src/modules/role_assignments/built_in_role/main.tf @@ -4,6 +4,7 @@ resource "azurerm_role_assignment" "main" { "${item.role_definition_name}-${item.resource_key}-${item.principal_type}-${item.principal}" => item }) + # ⬇️ Your scope logic – unchanged, gets the ID from var.resources scope = try( var.resources[ try(var.settings.lz_key, var.client_config.landingzone_key) @@ -25,13 +26,13 @@ resource "azurerm_role_assignment" "main" { # 2) NEW: users resolved from email/UPN : each.value.principal_type == "users_email" - ? data.azuread_user.users[each.value.principal].id + ? data.azuread_user.users[each.value.principal].object_id # 3) NEW: groups resolved from display_name : each.value.principal_type == "group_name" - ? data.azuread_group.groups[each.value.principal].id + ? data.azuread_group.groups[each.value.principal].object_id - # 4) Existing: resolve from var.resources (managed identities, etc.) + # 4) Existing: resolve from var.resources (managed_identities, etc.) : var.resources[ try(var.settings.lz_key, var.client_config.landingzone_key) ][each.value.principal_type][each.value.principal].principal_id, From 31ea041e0b3a75ea75369cd234b00abe669f68e7 Mon Sep 17 00:00:00 2001 From: Lyudmil Ilchev Date: Thu, 4 Dec 2025 17:31:54 +0200 Subject: [PATCH 4/6] add principal id --- .../role_assignments/built_in_role/main.tf | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/modules/role_assignments/built_in_role/main.tf b/src/modules/role_assignments/built_in_role/main.tf index 76bbad31..cf478c91 100644 --- a/src/modules/role_assignments/built_in_role/main.tf +++ b/src/modules/role_assignments/built_in_role/main.tf @@ -26,19 +26,31 @@ resource "azurerm_role_assignment" "main" { # 2) NEW: users resolved from email/UPN : each.value.principal_type == "users_email" - ? data.azuread_user.users[each.value.principal].object_id + ? data.azuread_user.users[each.value.principal].id # 3) NEW: groups resolved from display_name : each.value.principal_type == "group_name" - ? data.azuread_group.groups[each.value.principal].object_id + ? data.azuread_group.groups[each.value.principal].id - # 4) Existing: resolve from var.resources (managed_identities, etc.) + # 4) Fallback: resolve from var.resources (managed_identities etc.), + # with optional "lz_key/principal_name" syntax in principal. : var.resources[ - try(var.settings.lz_key, var.client_config.landingzone_key) - ][each.value.principal_type][each.value.principal].principal_id, + can(regex("/", each.value.principal)) + # if there is a "/", use the part before it as LZ key + ? split("/", each.value.principal)[0] + # otherwise use the default landing zone key + : try(var.settings.lz_key, var.client_config.landingzone_key) + ][each.value.principal_type][ + can(regex("/", each.value.principal)) + # if there is a "/", use the part after it as principal key + ? split("/", each.value.principal)[1] + # otherwise use the whole principal as key (current behavior) + : each.value.principal + ].principal_id, null ) + role_definition_name = each.value.role_definition_name } From 2bf3ed656b9d64709c58407d159bb0e1ebbccdc0 Mon Sep 17 00:00:00 2001 From: Lyudmil Ilchev Date: Thu, 4 Dec 2025 17:43:25 +0200 Subject: [PATCH 5/6] add principal id --- src/modules/role_assignments/built_in_role/main.tf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/role_assignments/built_in_role/main.tf b/src/modules/role_assignments/built_in_role/main.tf index cf478c91..b9aae98d 100644 --- a/src/modules/role_assignments/built_in_role/main.tf +++ b/src/modules/role_assignments/built_in_role/main.tf @@ -26,11 +26,11 @@ resource "azurerm_role_assignment" "main" { # 2) NEW: users resolved from email/UPN : each.value.principal_type == "users_email" - ? data.azuread_user.users[each.value.principal].id + ? data.azuread_user.users[each.value.principal].object_id # 3) NEW: groups resolved from display_name : each.value.principal_type == "group_name" - ? data.azuread_group.groups[each.value.principal].id + ? data.azuread_group.groups[each.value.principal].object_id # 4) Fallback: resolve from var.resources (managed_identities etc.), # with optional "lz_key/principal_name" syntax in principal. From 5247328894a0fd13ad072420ec08a3cd9450231e Mon Sep 17 00:00:00 2001 From: Lyudmil Ilchev Date: Thu, 4 Dec 2025 18:05:36 +0200 Subject: [PATCH 6/6] add principal id --- src/modules/role_assignments/built_in_role/_locals.tf | 6 +++--- src/modules/role_assignments/built_in_role/main.tf | 11 +---------- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/src/modules/role_assignments/built_in_role/_locals.tf b/src/modules/role_assignments/built_in_role/_locals.tf index 5b1e3a90..d58623ec 100644 --- a/src/modules/role_assignments/built_in_role/_locals.tf +++ b/src/modules/role_assignments/built_in_role/_locals.tf @@ -1,5 +1,5 @@ locals { - # Exactly your original flatten logic – NO var.resources, NO filtering + flat_assignments = flatten([ for role_definition_name, resources in var.settings : [ for resource_key, resource_details in resources : [ @@ -18,7 +18,7 @@ locals { ] ]) - # Distinct user emails (UPNs) + users_email = { for a in local.flat_assignments : a.principal => a.principal @@ -26,7 +26,7 @@ locals { && !can(regex("^[0-9a-fA-F-]{36}$", a.principal)) } - # Distinct group display names + group_names = { for a in local.flat_assignments : a.principal => a.principal diff --git a/src/modules/role_assignments/built_in_role/main.tf b/src/modules/role_assignments/built_in_role/main.tf index b9aae98d..2253faba 100644 --- a/src/modules/role_assignments/built_in_role/main.tf +++ b/src/modules/role_assignments/built_in_role/main.tf @@ -4,7 +4,6 @@ resource "azurerm_role_assignment" "main" { "${item.role_definition_name}-${item.resource_key}-${item.principal_type}-${item.principal}" => item }) - # ⬇️ Your scope logic – unchanged, gets the ID from var.resources scope = try( var.resources[ try(var.settings.lz_key, var.client_config.landingzone_key) @@ -20,31 +19,23 @@ resource "azurerm_role_assignment" "main" { ) principal_id = try( - # 1) Existing: direct object IDs each.value.principal_type == "object_ids" ? each.value.principal - # 2) NEW: users resolved from email/UPN : each.value.principal_type == "users_email" ? data.azuread_user.users[each.value.principal].object_id - # 3) NEW: groups resolved from display_name : each.value.principal_type == "group_name" ? data.azuread_group.groups[each.value.principal].object_id - # 4) Fallback: resolve from var.resources (managed_identities etc.), - # with optional "lz_key/principal_name" syntax in principal. + : var.resources[ can(regex("/", each.value.principal)) - # if there is a "/", use the part before it as LZ key ? split("/", each.value.principal)[0] - # otherwise use the default landing zone key : try(var.settings.lz_key, var.client_config.landingzone_key) ][each.value.principal_type][ can(regex("/", each.value.principal)) - # if there is a "/", use the part after it as principal key ? split("/", each.value.principal)[1] - # otherwise use the whole principal as key (current behavior) : each.value.principal ].principal_id, null