'Terraform - creating a list(string) for Azure locations

I'm trying to deploy a resource group to several locations using Terraform in Azure but get the error: "Inappropriate value for attribute "location": string required."

I'm very new to Terraform so I'm not sure why this doesn't work.

My code:

locals {
  webappsperloc = 4
}

resource "azurerm_resource_group" "rg" {
  count    = length(var.webapplocs)
  name     = "whatever${count.index}"
  location = var.webapplocs[count.index]
  tags     = var.tags

}

resource "random_string" "webapprnd" {
  length  = 8
  lower   = true
  number  = true
  upper   = false
  special = false
}

resource "azurerm_app_service_plan" "plan" {
  count               = length(var.webapplocs)
  name                = "whatever-${var.webapplocs[count.index]}"
  location            = var.webapplocs[count.index]
  resource_group_name = element(azurerm_resource_group.rg.*.name,count.index)
  tags                = var.tags

  sku {
    tier = "standard"
    size = "S1"
  }
}
resource "azurerm_app_service" "app" {
  count               = local.webappsperloc
  name                = format("webapp-%s-%02d-%s", random_string.webapprnd.result, count.index + 1, element(var.webapplocs, count.index))
  resource_group_name = element(azurerm_resource_group.rg.*.name,count.index)
  location            = element(var.webapplocs, count.index)
  app_service_plan_id = element(azurerm_app_service_plan.plan.*.id, count.index)
  https_only          = true

  site_config {
    dotnet_framework_version = "v4.0"
    always_on                = true
    ftps_state               = "Disabled"
    min_tls_version          = "1.2"
  }

  # tags = {
  #   environment = var.tags
  #   source      = "terraform"
  # }
}
variable "webapplocs" {
  default = ["northeurope", "westeurope", "eastus", "uksouth"]
}

#variable "loc" {
#  description = "Default Azure region"
#  default     = "northeurope"
#}

variable "tags" {
  default = {
    source = "Terraform"
    env    = "Dev"
  }
}
#terraform.tfvars


loc = "northeurope"

tags = {
  source = "terrafrom"
  env    = "Dev"
}

webapplocs = ["northeurope","westeurope","eastus","uksouth"]
terraform {
  required_providers {
    azure = {
      source  = "hashicorp/azurerm"
      version = "=2.76.0"
    }
  }
  backend "azurerm" {
    resource_group_name  = "xxxx"
    storage_account_name = "xxxx"
    container_name       = "xxxx"
    key                  = "xxxx"
  }
}

# azure provider
provider "azure" {
  features {}
}

If you need anything else please let me know. I've used this function before without issue.

Thanks.

edit: code updated to reflect what I managed to get working.



Solution 1:[1]

Based on the error you are getting, I think this is related to invalid attribute reference on this line:

location = var.location

The azurerm_resource_group resource requires only a single value for location and you are providing a list of strings (based on the location variable definition), so you get the error:

"Inappropriate value for attribute "location": string required."

Since you want resource groups in all regions (i.e., location), I would suggest using for_each [1] instead of count.

That way you will be able to have one of each resources per region. How this would work (and there might be more elegant ways of doing it):

resource "azurerm_resource_group" "rg" {
  for_each = toset(var.location)
  name     = "${each.value}-${var.resource_group_name}"
  location = each.value

  tags = {
    environment = var.environment
    source      = "terraform"
  }
}

resource "azurerm_app_service_plan" "plan" {
  for_each            = toset(var.location)
  name                = "${each.value}-${var.app_service_plan}"
  location            = azurerm_resource_group.rg[each.value].location # or alternatively each.value
  resource_group_name = azurerm_resource_group.rg[each.value].name


  sku {
    tier = "Standard"
    size = "S1"
  }
}

resource "azurerm_app_service" "app" {
  for_each            = toset(var.location)
  name                = "${each.value}-${var.app_service}"
  resource_group_name = azurerm_resource_group.rg[each.value].name
  location            = azurerm_resource_group.rg[each.value].location # or alternatively each.value
  app_service_plan_id = azurerm_app_service_plan.plan[each.value].id
  https_only          = true

  site_config {
    dotnet_framework_version = "v4.0"
    always_on                = true
    ftps_state               = "Disabled"
    min_tls_version          = "1.2"
  }

  tags = {
    environment = var.environment
    source      = "terraform"
  }
}

Note that I am using toset [2] here to convert the list to a set in order to be able to use for_each on it. Also I am using the values to make names of resources across regions more distinguishable, i.e., appending each.value to the variables you want to use for names.


[1] https://www.terraform.io/language/meta-arguments/for_each

[2] https://www.terraform.io/language/functions/toset

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1