Child resources created regardless of dependency reference

Hi,

I’d like to start of with mentioning that it’s been a while since I have done anything significant with Terraform. The last time was when TF was at version ~> 0.12.

I have a module that creates an Azure automation account. I want Terraform to create a user-assigned managed identity if the identity attribute’s value is set to UserAssigned. Currently, the user-assigned managed identity is created regardless of the identity attribute’s value.

Back in the day (~> 0.12) , my understanding was that resources wouldn’t be created unless it was referenced. Is this a change in behaviour (version 1.2.3) or am I doing something wrong?

I first tried adding my user_assigned_identity module to the azurerm_automation_account config file:

module "user_assigned_identity" {
  source                      = "app.terraform.io/myOrg/user_assigned_identity/azurerm"
  product_location            = var.product_location
  resource_group_object       = var.resource_group_object
  user_assigned_identity_data = var.automation_account_data
  common_tags                 = var.common_tags
}

locals {
  resource_type = "ata"
  resource_data = flatten([
    for product_key, product in var.automation_account_data : [
      for resource_key, resource in product : {
        base_resource_name = (
          format("%s%s%s%02d",
            local.resource_type,
            length(var.resource_group_object.environment_ids) == 1 ? var.resource_group_object.environment_ids[0] : var.resource_group_object.environment_ids[resource_key],
            resource.name != null ? resource.name : length(var.resource_group_object.product_ids)     == 1 ? var.resource_group_object.product_ids[0]     : var.resource_group_object.product_ids[product_key],
            product_key + 1
          ) 
        )
        resource_group_name           = var.resource_group_object.names[product_key]
        location                      = var.resource_group_object.locations[product_key]
        sku_name                      = resource.sku_name
        public_network_access_enabled = resource.public_network_access_enabled
        identity_type                 = resource.identity.type
        identity_ids                  = resource.identity.type == "UserAssigned" ? split(",", module.user_assigned_identity.resource_object["${product_key}.${resource_key}"].id) : null
        tags                          = merge(var.common_tags, resource.resource_tags)
        product_key                   = product_key
        resource_key                  = resource_key
        id                            = "${product_key}.${resource_key}"
      }
    ]
  ])
}

resource "azurerm_automation_account" "pool" {
  for_each = {
    for resource in local.resource_data :
    resource.id => resource
  }

  name                          = format("%s%02d", each.value.base_resource_name, each.value.resource_key + 1)
  location                      = each.value.location
  resource_group_name           = each.value.resource_group_name
  sku_name                      = each.value.sku_name
  public_network_access_enabled = each.value.public_network_access_enabled
  identity {
    type              = each.value.identity_type
    identity_ids = each.value.identity_ids
  }

  # dynamic identity {
  #   iterator = item
  #   for_each = each.value.identity[*]
  #   content {
  #     type         = item.value.type
  #     identity_ids = item.value.type == "UserAssigned" ? var.user_assigned_identity_object.resource_object[*].id : null
  #   }
  # }
  
  tags                          = each.value.tags
}

I then wondered if modules cannot be used in this way and/or it was the module’ declaration that was causing the resource to be created, even thought resource.identity_type = SystemAssigned. So, I referenced the azurerm_user_assigned_identity resource directly in the configuration file:

locals {
  resource_type = "ata"
  resource_data = flatten([
    for product_key, product in var.automation_account_data : [
      for resource_key, resource in product : {
        base_resource_name = (
          format("%s%s%s%02d",
            local.resource_type,
            length(var.resource_group_object.environment_ids) == 1 ? var.resource_group_object.environment_ids[0] : var.resource_group_object.environment_ids[resource_key],
            resource.name != null ? resource.name : length(var.resource_group_object.product_ids)     == 1 ? var.resource_group_object.product_ids[0]     : var.resource_group_object.product_ids[product_key],
            product_key + 1
          ) 
        )
        base_resource_name_umi = (
          format("%s%s%s%02d",
            "umi",
            length(var.resource_group_object.environment_ids) == 1 ? var.resource_group_object.environment_ids[0] : var.resource_group_object.environment_ids[resource_key],
            resource.name != null ? resource.name : length(var.resource_group_object.product_ids)     == 1 ? var.resource_group_object.product_ids[0]     : var.resource_group_object.product_ids[product_key],
            product_key + 1
          ) 
        )
        resource_group_name           = var.resource_group_object.names[product_key]
        location                      = var.resource_group_object.locations[product_key]
        sku_name                      = resource.sku_name
        public_network_access_enabled = resource.public_network_access_enabled
        identity_type                 = resource.identity.type
        #identity_ids                  = resource.identity.type == "UserAssigned" ? split(",", module.user_assigned_identity.resource_object["${product_key}.${resource_key}"].id) : null
        identity_ids                  = resource.identity.type == "UserAssigned" ? split(",", azurerm_user_assigned_identity.pool["${product_key}.${resource_key}"].id) : null
        tags                          = merge(var.common_tags, resource.resource_tags)
        product_key                   = product_key
        resource_key                  = resource_key
        id                            = "${product_key}.${resource_key}"
      }
    ]
  ])
}

resource "azurerm_automation_account" "pool" {
  for_each = {
    for resource in local.resource_data :
    resource.id => resource
  }

  name                          = format("%s%02d", each.value.base_resource_name, each.value.resource_key + 1)
  location                      = each.value.location
  resource_group_name           = each.value.resource_group_name
  sku_name                      = each.value.sku_name
  public_network_access_enabled = each.value.public_network_access_enabled
  identity {
    type = each.value.identity_type
    #identity_ids = each.value.identity_ids
    #identity_ids = null
    identity_ids = each.value.identity_type == "UserAssigned" ? split(",", azurerm_user_assigned_identity.pool[each.key].id) : null
  }

  # dynamic identity {
  #   iterator = item
  #   for_each = each.value.identity[*]
  #   content {
  #     type         = item.value.type
  #     identity_ids = item.value.type == "UserAssigned" ? var.user_assigned_identity_object.resource_object[*].id : null
  #   }
  # }
  
  tags                          = each.value.tags
}


resource "azurerm_user_assigned_identity" "pool" {
  for_each = {
    for resource in local.resource_data :
    resource.id => resource
  }
  name                          = format("%s%02d", each.value.base_resource_name_umi, each.value.resource_key + 1)
  location                      = each.value.location
  resource_group_name           = each.value.resource_group_name
  
  tags                          = each.value.tags
}

Even with identity.identity_ids = null it still creates the azurerm_user_assigned_identity resource. My understanding is that it shouldn’t.

If anyone can share some pointers on what’s going on, that would be awesome.

P.S. Ignore the references to TF Cloud, this is all run locally.

T. I. A.

When you write a resource block in the configuration, it signifies that you want to create a single instance of that resource. If you want to create 0 or more instances of that resource, you can use the count or for_each meta arguments to control the number of instances created. Whether a resource is referenced or not has no bearing on it’s creation.

I’m also observing that if I change the identity.type from SystemAssigned to UserAssigned, Azure still has SystemAssigned enabled. Documentation suggests, one or the other or both.

Hi @woter1832!

That follow-up question is about the Azure provider rather than Terraform Core, so you might have better luck if you ask about it in a separate topic in the Azure provider’s forum category, since otherwise folks who have Azure provider expertise might not think to look here to find Azure-specific questions. Alternatively, if you think what you’ve observed is a bug in the provider then you could open an bug report issue.

@jbardin,

Thanks. Of course that’s the way it works. Problem was between chair and keyboard, as per normal!