I’m having an hard time to understand why does my plan needs to replace a ressource.
Here is the context, I have a dictionnary of AAD groups, where the values are tags and custom roles I want to apply to them.
I have 2 of thoses dictionnaries, one to handle groups for Azure Marchine Learning Compute instances, where a create a managed identity in the module that will be linked to the ressource. The second one are groups that are allowed to have read access to databricks.
So I take the intersection of the 2 dicts to take only groups that will create me managed id that I will send to my databricks module and create a Service Principal linked to this managed ID.
And every time I get a new user in a group, it will create correctly the compute instance with its managed ID, without deleting the other compute instances of the group. But it will remove all the service principal of all groups in databricks, wait the creation of the compute instance, and re-add all service principals from all groups.
This is how I create the compute instances and managed ID
data "azuread_group" "all_groups_compute_instance" {
for_each = local.azuread_group_info
display_name = each.key
}
data "azuread_users" "all_users_compute_instance" {
for_each = data.azuread_group.all_groups_compute_instance
ignore_missing = true
object_ids = each.value.members
}
# Here I create aN azurerm_user_assigned_identity + add Rbac roles + create a azurerm_machine_learning_compute_instance with azurerm_user_assigned_identity linked to it.
module "azurerm_machine_learning_compute_instance" {
source = "./modules/aml_compute_instance_creation"
for_each = merge([
for group_name, group_data in local.azuread_group_info : {
for user in data.azuread_users.all_users_compute_instance[group_name].users : "${group_name}-${user.display_name}" => {
group = group_name
role = group_data.role
user = user
}
}
]...)
context = var.context
user = each.value.user
role = each.value.role
compute_instance_tags = local.azuread_group_info[each.value.group]["tags"]
ressoure_group = module.resource_group_01
azurerm_machine_learning_workspace = module.mlw_01
subnet_ressource = module.subnet_mlw
tenant_id = var.secrets.TENANT_ID
keyvault = module.keyvault_gb
tags = local.tags[var.context.environment]
}
This is how I create the SP in databricks, assign in a group in databricks.
# Take only that have a compute instance on AML AND roles on databricks
locals {
aml_groups_for_databricks = {
for key, group in local.groups :
key => group if contains(keys(local.azuread_group_info), key)
}
}
# local.groups is the dict for databricks roles
# local.azuread_group_info is the dict for AML compute instance creation
data "azuread_group" "all_groups_databricks_for_aml" {
for_each = local.aml_groups_for_databricks
display_name = each.key
}
data "azuread_users" "all_users_databricks_for_aml" {
for_each = data.azuread_group.all_groups_databricks_for_aml
ignore_missing = true
object_ids = each.value.members
}
# This module will only get the id of the managed identity, create it in databricks as a service principal and link it to a group that has the same name of they key group from previous group dict
module "compute_instance_policy" {
source = "./modules/databricks_aml_compute_policy"
for_each = merge([
for group_name, group_data in local.aml_groups_for_databricks : {
for user in data.azuread_users.all_users_databricks_for_aml[group_name].users : "${group_name}-${user.display_name}" => {
id = "${group_name}-${user.display_name}"
}
}
]...)
tenant_id = var.secrets.TENANT_ID
id = module.databricks_01.id
managed_identity_name = module.azurerm_machine_learning_compute_instance[each.key].managed_identity
# Custom dependency
# If I don't add this, it will try to create the SP without waiting for the managed_identity to be created from the azurerm_machine_learning_compute_instance module
dependency_resource = [module.azurerm_machine_learning_compute_instance[each.key]]
}
So everytime I plan/apply where there’s a modification in the number of users in one of the groups, Terraform reads all the ressources linked to other service principals in databricks, removes them, waits for the compute instance to be created (5-10 minutes) and then recreate all SP in databricks. Making all other user not able to connect to databricks from their compute instances.
# module.compute_instance_policy["USERNAME-GROUP_NAME"].data.azuread_service_principal.compute_managed_identity will be read during apply
# (depends on a resource or a module with changes pending)
<= data "azuread_service_principal" "compute_managed_identity" {
+ account_enabled = (known after apply)
+ alternative_names = (known after apply)
+ app_role_assignment_required = (known after apply)
+ app_role_ids = (known after apply)
+ app_roles = (known after apply)
+ application_id = (known after apply)
+ application_tenant_id = (known after apply)
+ client_id = (known after apply)
+ description = (known after apply)
+ display_name = "USERNAME-GROUP_NAME"
+ feature_tags = (known after apply)
+ features = (known after apply)
+ homepage_url = (known after apply)
+ id = (known after apply)
+ login_url = (known after apply)
+ logout_url = (known after apply)
+ notes = (known after apply)
+ notification_email_addresses = (known after apply)
+ oauth2_permission_scope_ids = (known after apply)
+ oauth2_permission_scopes = (known after apply)
+ object_id = (known after apply)
+ preferred_single_sign_on_mode = (known after apply)
+ redirect_uris = (known after apply)
+ saml_metadata_url = (known after apply)
+ saml_single_sign_on = (known after apply)
+ service_principal_names = (known after apply)
+ sign_in_audience = (known after apply)
+ tags = (known after apply)
+ type = (known after apply)
}
# module.compute_instance_policy["USERNAME-GROUP_NAME"].databricks_group_member.compute_group_sp must be replaced
-/+ resource "databricks_group_member" "compute_group_sp" {
~ id = "314248904982378|8705618699735534" -> (known after apply)
~ member_id = "8705618699735534" # forces replacement -> (known after apply) # forces replacement
# (1 unchanged attribute hidden)
}