For_each: conditional is not creating the new resource

Hello everyone!

I hope everyone is fine.

I would like to ask for your help, because I am working on a project and I have reached a situation that I am not able to find a solution.

I’m creating a terraform module project to create monitoring resources in Azure.

If an action group has already been created in Azure, its ID must be returned:

data.tf

data "azurerm_monitor_action_group" "default" {
  for_each = {
      for s in var.monitor : s.name => s 
      if s.action_group["create_action_group"] == false
  } 

  name                = each.value.action_group["action_group_name"]
  resource_group_name = each.value.action_group["resource_group_name"]
}

If the action group does not exist, it will be created:

main.tf

resource "azurerm_monitor_action_group" "default" {
  for_each = {
      for s in local.target_resource : s.action_group => s 
      if s.create_action_group == true
  }

  name                = each.value.action_group["action_group_name"]
  resource_group_name = each.value.action_group["resource_group_name"]
 short_name          = lookup(each.value.action_group["short_name"]
}

For that I created a variable create_action_group, if your value is set to true it creates the action group, if set to false it will get the ID in the date resource.

In the project I created a variable of type list(object) and one of the objects is the variable action_group which is of type map.

variables.tf

variable "monitor" {
  type        = list(object({
    name                   = optional(string)
    resource_group_name  = optional(string)
    target_resource_id    = optional(string)
    storage_account_id  = optional(string)
    workspace = optional(string)

    action_group = optional(map(string))

    eventhub_name = optional(string)
    eventhub_authorization_rule_id = optional(string)

    log_analytics_workspace_id = optional(string)

    partner_solution_id = optional(string)

    log = optional(list(object({
      category = optional(string)
      category_group  = optional(bool)
      retention_policy = optional(list(object({
        enabled = optional(bool)
        days    = optional(number)
      })))
    })))

    metric = optional(list(object({
      category = optional(string)
      enabled  = optional(bool)
      retention_policy = optional(list(object({
        enabled = optional(bool)
        days    = optional(number)
      })))
    })))
   
    action_group_name = optional(list(string))
    scopes              = optional(list(string))
    enabled             = optional(bool)
    auto_mitigate = optional(bool)
    frequency   = optional(string)
    severity    = optional(number)
    window_size = optional(string)

    criteria = optional(list(object({
      metric_namespace = optional(string)
      metric_name      = optional(string)
      aggregation      = optional(string)
      operator         = optional(string)
      threshold        = optional(number)
      skip_metric_validation = optional(bool)
      dimension = optional(list(object({
        name     = optional(string)
        operator = optional(string)
        values   = optional(list(string))
      })))
    })))

    dynamic_criteria = optional(list(object({
      metric_namespace = optional(string)
      metric_name      = optional(string)
      aggregation      = optional(string)
      operator         = optional(string)
      alert_sensitivity = optional(string)
      evaluation_total_count = optional(number)
      evaluation_failure_count = optional(number)
      ignore_data_before = optional(string)
      skip_metric_validation = optional(bool)
      dimension = optional(list(object({
        name     = optional(string)
        operator = optional(string)
        values   = optional(list(string))
      })))
    })))

    application_insights_web_test_location_availability_criteria = optional(list(object({
      web_test_id = optional(string)
      component_id      = optional(string)
      failed_location_count      = optional(string)      
    })))

    action = optional(list(object({
      action_group_id = optional(string)
      webhook_properties  = optional(map(string))
    })))
  }))
  #})))
  description = "(Optional) Map of key/value to configure `Blob Storage`."
  default     = []
}

In the project:

monitor.tf

module "monitoring" {
  
  source = "../azure-monitor"

  monitor = [
    {
      name = "myRule"
      resource_group_name = "my-rg-demo"
      target_resource_id    = "${module.storage.id[0]}/blobServices/default"
      storage_account_id =  "${module.storage.id[0]}"
      
      metric = [
        {
          category = "Transaction"
          enabled = true
          retention_policy = [
            {
              enabled = true
              days = 15
            }
          ]
        }
      ]
        
      log = [
        {
          category = "StorageRead"
          enabled = true
          retention_policy = [
            {
              enabled = true
              days = 15
            }
          ]
        },
        {
          category = "StorageWrite"
          enabled = true
          retention_policy = [
            {
              enabled = true
              days = 15
            }
          ]
        },
        {
          category = "StorageDelete"
          enabled = true
          retention_policy = [
            {
              enabled = true
              days = 15
            }
          ]
        }
      ]

      action_group = {
        create_action_group = false
        resource_group_name = "my-vm"
        action_group_name = "ag-servicenow"
      }

      action = [{}]

      frequency   = "PT1M"
      severity    = "3"
      window_size = "PT1H"
      scopes = [module.storage.id[0]]

      criteria = [{
        metric_namespace = "Microsoft.Storage/storageAccounts"
        metric_name = "UsedCapacity"
        aggregation = "Average"
        operator = "GreaterThan"
        threshold = 80
      }]      
    }
  ]    
  
  depends_on = [
    module.storage
  ]
}

When the create_action_group variable is set to false, it is returning the action group ID.

data "azurerm_monitor_action_group" "default" {
  for_each = {
      for s in var.monitor : s.name => s 
      if s.action_group["create_action_group"] == false
  } 

  name                = each.value.action_group["action_group_name"]
  resource_group_name = each.value.action_group["resource_group_name"]
}

But when the value of the create_action_group variable is set to true, the action group is not being created.

resource "azurerm_monitor_action_group" "default" {
  for_each = {
      for s in local.target_resource : s.action_group => s 
      if s.create_action_group == true
  }

  name                = each.value.action_group["action_group_name"]
  resource_group_name = each.value.action_group["resource_group_name"]
 short_name          = lookup(each.value.action_group["short_name"]
}

When running the code I get the following error:

╷
│ Error: Unsupported attribute
│
│   on .terraform/modules/monitoring/main.tf line 22, in resource "azurerm_monitor_action_group" "default":
│   22:       if s.create_action_group == true
│
│ This object does not have an attribute named "create_action_group".
╵
╷
│ Error: Unsupported attribute
│
│   on .terraform/modules/monitoring/main.tf line 22, in resource "azurerm_monitor_action_group" "default":
│   22:       if s.create_action_group == true
│
│ This object does not have an attribute named "create_action_group".

Can someone help me?

Because you have not revealed what local.target_resource is, it is not possible to understand where the issue may lie.

@maxb,

Thanks for the answer, sorry for forgetting, follow the local.tf configuration

locals.tf

locals {
  target_resource = { for s in var.monitor : s.name => s }
}

@maxb,

I identified the problem, when I use the for to iterate over the list the output of type a map(string):

local.tf

locals {
  monitoring = { for k, v in var.monitor : k => v.action_group["create_action_group"] }
}
  + monitor         = {
      + "0" = "false"
      + "1" = "false"
    }

But when I was setting the condition for resource creation, the expected response is a boolean value:

main.tf

resource "azurerm_monitor_action_group" "default" { 
  for_each = { 
      for n in var.monitor : n.name => n
      if n.action_group["create_action_group"] == true
    }

  name                = each.value.action_group["action_group_name"]
  resource_group_name = each.value.action_group["resource_group_name"]
  short_name          = substr(each.value.action_group["action_group_name"], 0, 12)

  tags = merge(
    var.runtime_tags,
    var.global.tags,
    lookup(each.value, "tags", null)
  )
}

I managed to solve the problem by changing the condition to string

main.tf

resource "azurerm_monitor_action_group" "default" { 
  for_each = { 
      for n in var.monitor : n.name => n
      if n.action_group["create_action_group"] == "true"
    }

  name                = each.value.action_group["action_group_name"]
  resource_group_name = each.value.action_group["resource_group_name"]
  short_name          = substr(each.value.action_group["action_group_name"], 0, 12)

  tags = merge(
    var.runtime_tags,
    var.global.tags,
    lookup(each.value, "tags", null)
 }

The change was in this part of the code:

if n.action_group["create_action_group"] == "true"

@maxb

Can you please help me with another question.

I need to create a condition for a dynamic block, if the resouce “${data.azurerm_monitor_action_group.default[each.key].id}”, return the action group ID it creates the action.

If the {data.azurerm_monitor_action_group.default[each.key].id}"**, is false it will get the ID of the action group in the resource **"{azurerm_monitor_action_group.default[each.key].id}”, I believe the ternary below, but it’s not working.

  dynamic "action" {
    for_each = each.value["action"] == null ? [] : each.value["action"]
    content {
      action_group_id = action.value["action_group_id"] == "" ? data.azurerm_monitor_action_group.default[each.key].id : azurerm_monitor_action_group.default[each.key].id
         webhook_properties      = lookup(action.value, "webhook_properties", {})
    }
  }
  dynamic "action" {
    for_each = each.value["action"] == null ? [] : each.value["action"]
    content {
      action_group_id = action.value["action_group_id"] == null ? data.azurerm_monitor_action_group.default[each.key].id : azurerm_monitor_action_group.default[each.key].id
      webhook_properties      = lookup(action.value, "webhook_properties", {})
    }
  }

I can have 2 possibilities:

I action_group_id, will receive the value of the data: data.azurerm_monitor_action_group.default[each.key].id

Or the resource that will be created: azurerm_monitor_action_group.default[each.key].id

When running the code I am getting the following error:

╷
│ Error: Invalid index
│
│   on .terraform/modules/monitoring/main.tf line 185, in resource "azurerm_monitor_metric_alert" "default":
│  185:       action_group_id =  action.value["action_group_id"] == null ? "${data.azurerm_monitor_action_group.default[each.key].id}" : "${azurerm_monitor_action_group.default[each.key].id}" # <-- funcionando
│     ├────────────────
│     │ data.azurerm_monitor_action_group.default is object with no attributes
│     │ each.key is "myRule"
│
│ The given key does not identify an element in this collection value.
╵
╷
│ Error: Invalid index
│
│   on .terraform/modules/monitoring/main.tf line 185, in resource "azurerm_monitor_metric_alert" "default":
│  185:       action_group_id =  action.value["action_group_id"] == null ? "${data.azurerm_monitor_action_group.default[each.key].id}" : "${azurerm_monitor_action_group.default[each.key].id}" # <-- funcionando
│     ├────────────────
│     │ data.azurerm_monitor_action_group.default is object with no attributes
│     │ each.key is "myRule-01"
│
│ The given key does not identify an element in this collection value.

When I run them separately it works normally.

  dynamic "action" {
    or_each = each.value["action"] == null ? [] : each.value["action"]
    content {
      action_group_id = data.azurerm_monitor_action_group.default[each.key].id 
      webhook_properties      = lookup(action.value, "webhook_properties", {})
    }
  }

Or

  dynamic "action" {
    or_each = each.value["action"] == null ? [] : each.value["action"]
    content {
      action_group_id = azurerm_monitor_action_group.default[each.value].id #
      webhook_properties      = lookup(action.value, "webhook_properties", {})
    }
  }

Thanks for your help.

Oh. Right, you shouldn’t be defining action_group as

if you intend to populate it with

You should change your type declaration to match how you’re actually writing your input data, i.e.

action_group = object({
        create_action_group = bool
        resource_group_name = string
        action_group_name = string
})

(Add optional(...) as you prefer.)

Please see Welcome to the forum - please reformat your message

Especially the bit of your message with italics and weird subscript letters.

That aside, however, I am still finding I cannot help you effectively because you are only showing tiny parts of your configuration.

The error message is pretty clear though:

when there are no instances of data.azurerm_monitor_action_group.default because its for_each is empty, then it follows that you cannot evaluate data.azurerm_monitor_action_group.default[each.key].

Hi @maxb!

Thank you for your response, when I statement the variable as object as your suggestion, the output is a bool value instead string.

Changes to Outputs:
  + monitor = {
      + "0" = false
      + "1" = false
    }

I rewite my question, sory by inconvenient.

If variable create_action_group is false, data azurerm_monitor_action_group get Id of the action groups.

If variable create_action_group is true, a new resource azurerm_monitor_action_group get is created.

I need pass this resource to the block action, for this I’m creating the folloing ternary:

  dynamic "action" {
    for_each = each.value["action"] == null ? [] : each.value["action"]
    content {
      action_group_id = action.value["action_group_id"] == "" ? data.azurerm_monitor_action_group.default[each.key].id : azurerm_monitor_action_group.default[each.key].id
         webhook_properties      = lookup(action.value, "webhook_properties", {})
    }
  }
  dynamic "action" {
    for_each = each.value["action"] == null ? [] : each.value["action"]
    content {
      action_group_id = action.value["action_group_id"] == null ? data.azurerm_monitor_action_group.default[each.key].id : azurerm_monitor_action_group.default[each.key].id
      webhook_properties      = lookup(action.value, "webhook_properties", {})
    }
  }

But I received the error:

│ Error: Invalid index
│
│   on .terraform/modules/monitoring/main.tf line 155, in resource "azurerm_monitor_metric_alert" "default":
│  155:       action_group_id =  local.monitoring == false ? "${data.azurerm_monitor_action_group.default[each.key].id}" : "${azurerm_monitor_action_group.default[each.key].id}" 
│     ├────────────────
│     │ azurerm_monitor_action_group.default is object with no attributes
│     │ each.key is "myRule-01"
│
│ The given key does not identify an element in this collection value.
╵
╷
│ Error: Invalid index
│
│   on .terraform/modules/monitoring/main.tf line 155, in resource "azurerm_monitor_metric_alert" "default":
│  155:       action_group_id =  local.monitoring == false ? "${data.azurerm_monitor_action_group.default[each.key].id}" : "${azurerm_monitor_action_group.default[each.key].id}"
│     ├────────────────
│     │ azurerm_monitor_action_group.default is object with no attributes
│     │ each.key is "myRule"
│
│ The given key does not identify an element in this collection value.

Could tell me please, if the description of my doubt improved?