Promise Preston
Promise Preston

Reputation: 28800

Terraform: For each loop for multiple objects within a block in a resource

I'm trying to set up multiple autoscaling rules for an Azure App Service Plan using Terraform.

resource "azurerm_resource_group" "example" {
  name     = "autoscalingTest"
  location = "West Europe"
}

resource "azurerm_virtual_machine_scale_set" "example" {
  # ...
}

resource "azurerm_monitor_autoscale_setting" "example" {
  name                = "myAutoscaleSetting"
  resource_group_name = azurerm_resource_group.example.name
  location            = azurerm_resource_group.example.location
  target_resource_id  = azurerm_virtual_machine_scale_set.example.id

  profile {
    name = "defaultProfile"

    capacity {
      default = 1
      minimum = 1
      maximum = 10
    }

    rule {
      metric_trigger {
        metric_name        = "Percentage CPU"
        metric_resource_id = azurerm_virtual_machine_scale_set.example.id
        time_grain         = "PT1M"
        statistic          = "Average"
        time_window        = "PT5M"
        time_aggregation   = "Average"
        operator           = "GreaterThan"
        threshold          = 75
        metric_namespace   = "microsoft.compute/virtualmachinescalesets"
        dimensions {
          name     = "AppName"
          operator = "Equals"
          values   = ["App1"]
        }
      }

      scale_action {
        direction = "Increase"
        type      = "ChangeCount"
        value     = "1"
        cooldown  = "PT1M"
      }
    }

    rule {
      metric_trigger {
        metric_name        = "Percentage CPU"
        metric_resource_id = azurerm_virtual_machine_scale_set.example.id
        time_grain         = "PT1M"
        statistic          = "Average"
        time_window        = "PT5M"
        time_aggregation   = "Average"
        operator           = "LessThan"
        threshold          = 25
      }

      scale_action {
        direction = "Decrease"
        type      = "ChangeCount"
        value     = "1"
        cooldown  = "PT1M"
      }
    }
  }

  notification {
    email {
      send_to_subscription_administrator    = true
      send_to_subscription_co_administrator = true
      custom_emails                         = ["[email protected]"]
    }
  }
}

Basically, I do not want to repeat the rules. So I will just define one block for the rules and use the variables file to set the values for the multiple rules.

This is what I have so far, but it's not working:

main.tf file

resource "azurerm_monitor_autoscale_setting" "main" {
  name                = var.monitor_autoscale_setting_name
  resource_group_name = var.resource_group_name
  location            = var.resource_group_location
  target_resource_id  = var.target_resource_id
  enabled             = var.monitor_autoscale_setting_enabled

  profile {
    name = var.autoscale_profile_name

    capacity {
      default = var.profile_capacity_default
      minimum = var.profile_capacity_minimum
      maximum = var.profile_capacity_maximum
    }

    for_each = var.rules
    metric_trigger {
      metric_name        = each.value["metric_name"]
      metric_resource_id = each.value["metric_resource_id"]
      time_grain         = each.value["metric_time_grain"]
      statistic          = each.value["metric_statistic"]
      time_window        = each.value["metric_time_window"]
      time_aggregation   = each.value["metric_time_aggregation"]
      operator           = each.value["metric_operator"]
      threshold          = each.value["metric_threshold"]
    }

    scale_action {
      direction = each.value["scale_action_direction"]
      type      = each.value["scale_action_type"]
      value     = each.value["scale_action_value"]
      cooldown  = each.value["scale_action_cooldown"]
    }

  notification {
    email {
      send_to_subscription_administrator    = var.send_to_subscription_administrator
      send_to_subscription_co_administrator = var.send_to_subscription_co_administrator
      custom_emails                         = var.notification_custom_emails
    }
  }
}

module file

module "monitor_autoscale_setting" {
  source                            = "../../../modules/azure/monitor-autoscale-setting"
  monitor_autoscale_setting_name    = var.monitor_autoscale_setting_name
  resource_group_name               = var.resource_group_name
  resource_group_location           = var.resource_group_location
  target_resource_id                = var.app_service_plan_id
  monitor_autoscale_setting_enabled = var.monitor_autoscale_setting_enabled
  autoscale_profile_name            = var.autoscale_profile_name
  profile_capacity_default          = var.profile_capacity_default
  profile_capacity_minimum          = var.profile_capacity_minimum
  profile_capacity_maximum          = var.profile_capacity_maximum

  rules = {
    "rule1" = {
      metric_trigger = {
        metric_name             = var.metric_name
        metric_resource_id      = var.app_service_plan_id
        metric_time_grain       = var.metric_time_grain
        metric_statistic        = var.metric_statistic
        metric_time_window      = var.metric_time_window
        metric_time_aggregation = var.metric_time_aggregation
        metric_operator         = var.metric_operator
        metric_threshold        = var.metric_threshold
      }
      scale_action = {
        scale_action_direction = var.scale_action_direction
        scale_action_type      = var.scale_action_type
        scale_action_value     = var.scale_action_value
        scale_action_cooldown  = var.scale_action_cooldown
      }
    }
    "rule2" = {
      metric_name             = var.metric_name
      metric_resource_id      = var.app_service_plan_id
      metric_time_grain       = var.metric_time_grain
      metric_statistic        = var.metric_statistic
      metric_time_window      = var.metric_time_window
      metric_time_aggregation = var.metric_time_aggregation
      metric_operator         = var.metric_operator
      metric_threshold        = var.metric_threshold
      scale_action_direction  = var.scale_action_direction
      scale_action_type       = var.scale_action_type
      scale_action_value      = var.scale_action_value
      scale_action_cooldown   = var.scale_action_cooldown
    }
  }
}

variables.tf file

variable "rules" {
  type = map(object({
    metric_trigger = {
      metric_name             = string
      metric_time_grain       = string
      metric_statistic        = string
      metric_time_window      = string
      metric_time_aggregation = string
      metric_operator         = string
      metric_threshold        = number
    }
    scale_action = {
      scale_action_direction = string
      scale_action_type      = string
      scale_action_value     = string
      scale_action_cooldown  = string
    }
  }))
  default = {
    "rule1" = {
      metric_trigger = {
        metric_name             = "Percentage CPU"
        metric_time_grain       = "PT1M"
        metric_statistic        = "Average"
        metric_time_window      = "PT5M"
        metric_time_aggregation = "Average"
        metric_operator         = "GreaterThan"
        metric_threshold        = 90
      }
      scale_action = {
        scale_action_direction = "Increase"
        scale_action_type      = "ChangeCount"
        scale_action_value     = "1"
        scale_action_cooldown  = "PT1M"
      }
    }
    "rule2" = {
      metric_trigger = {
        metric_name             = "Percentage CPU"
        metric_time_grain       = "PT1M"
        metric_statistic        = "Average"
        metric_time_window      = "PT5M"
        metric_time_aggregation = "Average"
        metric_operator         = "LessThan"
        metric_threshold        = 10
      }
      scale_action = {
        scale_action_direction = "Decrease"
        scale_action_type      = "ChangeCount"
        scale_action_value     = "1"
        scale_action_cooldown  = "PT1M"
      }
    }
  }
}

variable "monitor_autoscale_setting_name" {
  type        = string
  description = "The name of the AutoScale Setting"
}

variable "resource_group_name" {
  type        = string
  description = "The name of the resource group in which to create the App Service Plan component"
}

variable "resource_group_location" {
  type        = string
  description = "The supported Azure location where the resource exists"
}

variable "target_resource_id" {
  type        = string
  description = "Specifies the resource ID of the resource that the autoscale setting should be added to"
}

variable "monitor_autoscale_setting_enabled" {
  type        = bool
  description = "Specifies whether automatic scaling is enabled for the target resource. Defaults to true"
}

variable "autoscale_profile_name" {
  type        = string
  description = "The name of the profile"
}

variable "profile_capacity_default" {
  type        = number
  description = "The number of instances that are available for scaling if metrics are not available for evaluation"
}

variable "profile_capacity_minimum" {
  type        = number
  description = "The minimum number of instances for this resource. Valid values are between 0 and 1000."
}

variable "profile_capacity_maximum" {
  type        = number
  description = "The maximum number of instances for this resource. Valid values are between 0 and 1000"
}

Here are the errors:

│ Error: Invalid type specification
│ 
│   on variables.tf line 162, in variable "rules":
│  162:     metric_trigger = {
│  163:       metric_name             = string
│  164:       metric_time_grain       = string
│  165:       metric_statistic        = string
│  166:       metric_time_window      = string
│  167:       metric_time_aggregation = string
│  168:       metric_operator         = string
│  169:       metric_threshold        = number
│  170:     }
│ 
│ A type specification is either a primitive type keyword (bool, number, string) or a complex type constructor call, like list(string).
╵
╷
│ Error: Invalid type specification
│ 
│   on variables.tf line 171, in variable "rules":
│  171:     scale_action = {
│  172:       scale_action_direction = string
│  173:       scale_action_type      = string
│  174:       scale_action_value     = string
│  175:       scale_action_cooldown  = string
│  176:     }
│ 
│ A type specification is either a primitive type keyword (bool, number, string) or a complex type constructor call, like list(string).

Any form of help will be appreciated.

Upvotes: 0

Views: 3552

Answers (1)

Marcin
Marcin

Reputation: 238081

The correct type for your var.rules is as below (you forgot object):

variable "rules" {
  type = map(object({
    metric_trigger = object({
      metric_name             = string
      metric_time_grain       = string
      metric_statistic        = string
      metric_time_window      = string
      metric_time_aggregation = string
      metric_operator         = string
      metric_threshold        = number
    })
    scale_action = object({
      scale_action_direction = string
      scale_action_type      = string
      scale_action_value     = string
      scale_action_cooldown  = string
    })
  }))
  default = {
    "rule1" = {
      metric_trigger = {
        metric_name             = "Percentage CPU"
        metric_time_grain       = "PT1M"
        metric_statistic        = "Average"
        metric_time_window      = "PT5M"
        metric_time_aggregation = "Average"
        metric_operator         = "GreaterThan"
        metric_threshold        = 90
      }
      scale_action = {
        scale_action_direction = "Increase"
        scale_action_type      = "ChangeCount"
        scale_action_value     = "1"
        scale_action_cooldown  = "PT1M"
      }
    }
    "rule2" = {
      metric_trigger = {
        metric_name             = "Percentage CPU"
        metric_time_grain       = "PT1M"
        metric_statistic        = "Average"
        metric_time_window      = "PT5M"
        metric_time_aggregation = "Average"
        metric_operator         = "LessThan"
        metric_threshold        = 10
      }
      scale_action = {
        scale_action_direction = "Decrease"
        scale_action_type      = "ChangeCount"
        scale_action_value     = "1"
        scale_action_cooldown  = "PT1M"
      }
    }
  }
}

Upvotes: 1

Related Questions