Unable to access the for_each value inside the dynamic block

Unable to concat the variable value of the top for loop with the resource to get the Id

Error

│ Error: Can not parse "data_protection_snapshot_policy.0.snapshot_policy_id" as a resource id: Cannot parse Azure ID: parse "azurerm_netapp_snapshot_policy.snapshot_pol2_daily_to_monthly.id": invalid URI for request
│
│   with azurerm_netapp_volume.anf["1"],
│   on main.tf line 64, in resource "azurerm_netapp_volume" "anf":
│   64: resource "azurerm_netapp_volume" "anf" {
│
╵
╷
│ Error: Can not parse "data_protection_snapshot_policy.0.snapshot_policy_id" as a resource id: Cannot parse Azure ID: parse "azurerm_netapp_snapshot_policy.snapshot_pol2_daily_to_monthly.id": invalid URI for request
│
│   with azurerm_netapp_volume.anf["0"],
│   on main.tf line 64, in resource "azurerm_netapp_volume" "anf":
│   64: resource "azurerm_netapp_volume" "anf" {

Tried 2 options, both didn’t work

Option 1

variable "anf_volume" {
  default     = null
  description = "Az NetApp Volume"
  type = list(
    object(
      {
        name                       = string
        network_features           = string # Basic / Standard (Recommended)
        protocols                  = list(string)
        storage_quota_in_gb        = number # In GB, Min 100
        anf_cpool_name             = string #Note: Same as anf_cpool.name
        anf_cpool_service_level    = string #Note: Same as anf_cpool.service_level
        snapshot_directory_visible = bool
        snapshot_policy_name       = object({name = string}) # snapshot_pol2_daily_to_monthly, snapshot_pol1_daily_to_weekly
      }
    )
  )
}


resource "azurerm_netapp_volume" "anf" {
  lifecycle {
    prevent_destroy = true
    ignore_changes = [
      # Ignore changes to tags, e.g. because a management agent
      # updates these based on some ruleset managed elsewhere.
      tags,
      zone
    ]
  }

  for_each                   = { for idx, val in var.anf_volume : idx => val }
  name                       = each.value.name
  location                   = data.azurerm_resource_group.anf.location
  resource_group_name        = data.azurerm_resource_group.anf.name
  account_name               = azurerm_netapp_account.anf.name
  pool_name                  = each.value.anf_cpool_name
  volume_path                = each.value.name
  service_level              = each.value.anf_cpool_service_level
  subnet_id                  = data.azurerm_subnet.anf.id
  network_features           = each.value.network_features
  protocols                  = each.value.protocols
  storage_quota_in_gb        = each.value.storage_quota_in_gb
  snapshot_directory_visible = each.value.snapshot_directory_visible

  tags = var.default_tags

  dynamic "data_protection_snapshot_policy" {
    for_each = { for sp, sv in each.value.snapshot_policy_name : sp => sv  if startswith(sv, "snapshot")}
    content {
      snapshot_policy_id = "azurerm_netapp_snapshot_policy.${data_protection_snapshot_policy.value}.id"
    }
  }

  depends_on = [ 
    azurerm_netapp_pool.anf, 
    azurerm_netapp_snapshot_policy.snapshot_pol1_daily_to_weekly, 
    azurerm_netapp_snapshot_policy.snapshot_pol2_daily_to_monthly 
  ]

}

resource "azurerm_netapp_snapshot_policy" "snapshot_pol1_daily_to_weekly" {
<Truncated>
}


resource "azurerm_netapp_snapshot_policy" "snapshot_pol2_daily_to_monthly " {
<Truncated>
}

Option 2


variable "anf_volume" {
  default     = null
  description = "Az NetApp Volume"
  type = list(
    object(
      {
        name                       = string
        network_features           = string # Basic / Standard (Recommended)
        protocols                  = list(string)
        storage_quota_in_gb        = number # In GB, Min 100
        anf_cpool_name             = string #Note: Changing this forces a new resource to be created
        anf_cpool_service_level    = string
        snapshot_directory_visible = bool
        snapshot_policy_name       = string # snapshot_pol2_daily_to_monthly, snapshot_pol1_daily_to_weekly
      }
    )
  )
}


resource "azurerm_netapp_volume" "anf" {
  lifecycle {
    prevent_destroy = true
    ignore_changes = [
      # Ignore changes to tags, e.g. because a management agent
      # updates these based on some ruleset managed elsewhere.
      tags,
      zone
    ]
  }

  for_each                   = { for idx, val in var.anf_volume : idx => val }
  name                       = each.value.name
  location                   = data.azurerm_resource_group.anf.location
  resource_group_name        = data.azurerm_resource_group.anf.name
  account_name               = azurerm_netapp_account.anf.name
  pool_name                  = each.value.anf_cpool_name
  volume_path                = each.value.name
  service_level              = each.value.anf_cpool_service_level
  subnet_id                  = data.azurerm_subnet.anf.id
  network_features           = each.value.network_features
  protocols                  = each.value.protocols
  storage_quota_in_gb        = each.value.storage_quota_in_gb
  snapshot_directory_visible = each.value.snapshot_directory_visible

  tags = var.default_tags

  dynamic "data_protection_snapshot_policy" {
    for_each = each.value.snapshot_policy_name != null ? [1] : []
    content {
      snapshot_policy_id = "azurerm_netapp_snapshot_policy.${each.value.snapshot_policy_name}.id"
    }
  }

  depends_on = [ 
    azurerm_netapp_pool.anf, 
    azurerm_netapp_snapshot_policy.snapshot_pol1_daily_to_weekly, 
    azurerm_netapp_snapshot_policy.snapshot_pol2_daily_to_monthly 
  ]

}

resource "azurerm_netapp_snapshot_policy" "snapshot_pol1_daily_to_weekly" {
<Truncated>
}


resource "azurerm_netapp_snapshot_policy" "snapshot_pol2_daily_to_monthly " {
<Truncated>
}

Using for_each at resource level and using dynamic block as it has a mandatory field to bypass if the snapshot_policy is null or no

OK, so, the first problem is that what you’ve written here:

is literally sending the text string azurerm_netapp_snapshot_policy.some_value.id to the Azure Terraform provider, to be treated as an Azure resource. Which it isn’t. Get rid of the enclosing quotes.

That will lead you to the second problem… Terraform does NOT support dynamic references to a resource block.

There’s a choice of workarounds you can employ:

  1. Restructure all resource instances to be a single block (which can be referenced statically) plus for_each:
resource "azurerm_netapp_snapshot_policy" "policy" {
  for_each = {
    snapshot_pol1_daily_to_weekly = {
      # Specify configuration here
    }
    # ...
  }

  # Assign each argument from each.value...
}
      snapshot_policy_id = azurerm_netapp_snapshot_policy.policy[data_protection_snapshot_policy.value].id
  1. Lookup table
locals {
  policy_lookup_table = {
    snapshot_pol1_daily_to_weekly = azurerm_netapp_snapshot_policy.snapshot_pol1_daily_to_weekly
    # ...
  }
}
      snapshot_policy_id = local.policy_lookup_table[data_protection_snapshot_policy.value].id

@maxb Thanks a lot, you saved my day.
Lookup method worked.