Ok.. so..
I have an issue where main.tf calls one module (appservice.tf) which has an output that is then relied upon by another module (roleassignment.tf)
In my case… from main.tf I call my appservice.tf
module "contractreview_apibackendappservice" {
source = "./appservice"
app_service_plan_id = module.appserviceplan.app_service_plan_id
resource_group_name = module.vnet.resource_group_name
location = var.location
app_service_name = "zettle-${var.environment_name}-contracts-api"
subnet_id = module.vnet.defaultsubnet_id
}
appservice.tf code here below, we create a managed identity
resource "azurerm_windows_web_app" "appservice" {
name = var.app_service_name
resource_group_name = var.resource_group_name
location = var.location
service_plan_id = var.app_service_plan_id
https_only = true
identity {
type = "SystemAssigned"
}
site_config {
http2_enabled = true
websockets_enabled = true
always_on = true
vnet_route_all_enabled = true
use_32_bit_worker = false
}
virtual_network_subnet_id = var.subnet_id
}
appservice.tf output variable:
output "principal_id_of_managed_identity" {
value = azurerm_windows_web_app.appservice.identity[0].principal_id
}
Back in main.tf I reference that variable
locals {
rows_for_storage_account = [
{
role_definition_name = "Storage Blob Data Contributor"
principal_id = module.contractreview_apibackendappservice.principal_id_of_managed_identity
},
{
role_definition_name = "Storage Queue Data Contributor"
principal_id = module.contractreview_apibackendappservice.principal_id_of_managed_identity
}
]
}
module "roleassignment_for_api_storage" {
source = "./roleassignment"
for_each = { for row in local.rows_for_storage_account : "${row.role_definition_name}-${row.principal_id}" => row }
scope = module.storageaccount.storage_account_id
role_definition_name = each.value.role_definition_name
principal_id_of_managed_identity_to_give_role_assignment_to = each.value.principal_id
app_service_name = module.contractreview_apibackendappservice.app_service_name
depends_on = [ module.contractreview_apibackendappservice ]
}
But it errors when terraform plan runs.. roleassignment module is looking for the value of the principal id that hasn’t been established yet.
│ Error: Invalid template interpolation value
│ on main.tf line 138, in module "roleassignment_for_api_storage":
│ 138: for_each = { for row in local.rows_for_storage_account : "${row.role_definition_name}-${row.principal_id}" => row }
│ │ row.principal_id is null
│ The expression result is null. Cannot include a null value in a string template.
How can I get the roleassignment module to only run once the principal_id_of_managed_identity is established / known?
I know I can do one terraform plan/apply to get the managed principal set up / established and then uncomment the roleassignment module and run a second plan/apply for the role assignment creation but this is obviously a hack of the highest order. This works but clearly sucks.
I guess, I need some sort of way to create a dependency between the two? Currently terraform isn’t recognising the dependency on it’s own or the fact that the principal id is to be established when the apply is run.
I’m using azurerm 3.100.0 if that’s relevant
Thanks 🙂