sreeram
sreeram

Reputation: 61

Looping in for_each nested resources with terraform

I am trying to provision Multiple Azure service bus topics with Multiple subscriptions. I am able to create topics but am not able to loop over subscription variables to create subscriptions.

################

locals {
  servicebus = {
    "topic_1" = [{
      subscription = ["subscription1", "subscription2", "subscription3"]
    }],
    "topic_2" = [{
      subscription = ["subscription4", "subscription5", "subscription6"]
    }],
 "topic_3" = [{
      subscription = ["subscription7", "subscription8", "subscription9"]
    }]
  }

service_bus = flatten([
    for topicname, topic in local.servicebus : [
      for subname in topic : {
        name                = topicname
        subscription_name  = subname.subscription
      }
    ]
  ])
}

In servicebus_subscription resource block, unable to loop the subscription name but when I provide with index(each.value.subscription_name[0]), it is creating only one subscription

############  Creating Servicebus Topic ############################### 
module "servicebus_topic" {
  source                = "./servicebus/topic"
 for_each = {
    for sname in local.service_bus : sname.name => sname
  }  
  name                = each.key
  resource_group_name   = azurerm_resource_group.rg.name
  namespace_name        = module.servicebus_namespace.name
  max_size_in_megabytes = "1024"
  depends_on            = [module.servicebus_namespace.name]
}

##########  Creating Servicebus Subscription ############################### 
resource "azurerm_servicebus_subscription" "sbs" {  
  for_each = {
    for sname in local.service_bus : sname.name => sname
  }  
  name                = each.value.subscription_name
  topic_name          = module.servicebus_topic[each.value.name].name
  namespace_name      = module.servicebus_namespace.name
  resource_group_name = azurerm_resource_group.rg.name
  max_delivery_count  = "10"
}

Error:

Error: Incorrect attribute value type
│
│   on servicebus.tf line 77, in resource "azurerm_servicebus_subscription" "sbs":
│   77:   name                = each.value.subscription_name
│     ├────────────────
│     │ each.value.subscription_name is tuple with 3 elements
│
│ Inappropriate value for attribute "name": string required.
╵
╷
│ Error: Incorrect attribute value type
│
│   on servicebus.tf line 77, in resource "azurerm_servicebus_subscription" "sbs":
│   77:   name                = each.value.subscription_name
│     ├────────────────
│     │ each.value.subscription_name is tuple with 3 elements
│
│ Inappropriate value for attribute "name": string required.

local variable debug output with Terraform console:

> local.service_bus
[
  {
    "name" = "topic_1"
    "subscription_name" = [
      "subscription1",
      "subscription2",
      "subscription3",
    ]
  },
  {
    "name" = "topic_2"
    "subscription_name" = [
      "subscription4",
      "subscription5",
      "subscription16",
    ]
  },
]

Upvotes: 4

Views: 6299

Answers (2)

Marco
Marco

Reputation: 21

I have created a complete working sample based on the code of Marcin:


locals {
  servicebus = {
    "topic1" = [{
      subscription = ["sub1", "sub2"]
    }],
    "topic2" = [{
      subscription = ["sub1"]
    }]
  }

service_bus = merge([
    for topicname, topic in local.servicebus : {
      for subname in topic[0].subscription :
          "${topicname}-${subname}" => {
            name               = topicname
            subscription_name  =  subname
          }
    }
   ]...)
}

resource "azurerm_servicebus_topic" "topic" {
  depends_on = [
    module.servicebus
   ]

for_each = local.service_bus

  name                = each.value.name
  resource_group_name = azurerm_resource_group.shared.name
  namespace_name      = module.servicebus.namespace_name
  enable_partitioning = false
  max_size_in_megabytes = 1024
}

resource "azurerm_servicebus_subscription" "subscription" {  
  for_each = local.service_bus
   
  name                = each.value.subscription_name
  topic_name          = each.value.name
  namespace_name      = module.servicebus.namespace_name
  resource_group_name = azurerm_resource_group.shared.name
  max_delivery_count  = "10"
}

The only small issue is that it wont create the topic if there is no subscription. I have some topics where this is the case.

Upvotes: 2

Marcin
Marcin

Reputation: 238051

You were very close. It should be:

service_bus = merge([
    for topicname, topic in local.servicebus : {
      for subname in topic[0].subscription :
          "${topicname}-${subname}" => {
            name               = topicname
            subscription_name  =  subname
          }
    }
  ]...)

then

resource "azurerm_servicebus_subscription" "sbs" {  
  
  for_each = local.service_bus
  
  name                = each.value.subscription_name
  topic_name          = module.servicebus_topic[each.value.name].name
  namespace_name      = module.servicebus_namespace.name
  resource_group_name = azurerm_resource_group.rg.name
  max_delivery_count  = "10"
}

Upvotes: 2

Related Questions