1

I added a piece of code to support enable/disable to the feature - I used the count. Now I have the following problem:

ERROR:
on modules/eventhub/main.tf line 110, in resource "azurerm_eventhub" "events":

 110:   namespace_name      = azurerm_eventhub_namespace.eventhub.*.name
    |----------------
    | azurerm_eventhub_namespace.eventhub is tuple with 1 element

Inappropriate value for attribute "namespace_name": string required.

# Create EventHub namespace
resource "azurerm_eventhub_namespace" "eventhub" {
  count               = var.enable_eh ? 1 : 0
  name                  = "${var.name}-ns"
  location              = var.location
  resource_group_name   = var.resource_group
  sku                   = var.sku
  capacity              = var.capacity
  dedicated_cluster_id = azurerm_eventhub_cluster.eventhub[count.index].id #for eventHub Cluster
  auto_inflate_enabled     = var.auto_inflate != null ? var.auto_inflate.enabled : null
  maximum_throughput_units = var.auto_inflate != null ? var.auto_inflate.maximum_throughput_units : null

    dynamic "network_rulesets" {
    for_each = var.network_rules != null ? ["true"] : []
    content {
      default_action = "Deny"
      dynamic "ip_rule" {
        for_each = var.network_rules.ip_rules
        iterator = iprule
        content {
          ip_mask = iprule.value
        }
      }

      dynamic "virtual_network_rule" {
        for_each = var.network_rules.subnet_ids
        iterator = subnet
        content {
          subnet_id = subnet.value
        }
      }
    }
  }

  tags = var.tags
}

resource "azurerm_eventhub_namespace_authorization_rule" "events" {
  for_each            = local.authorization_rules
  name                = each.key
  namespace_name      = azurerm_eventhub_namespace.eventhub.*.name
  resource_group_name = var.resource_group

  listen = each.value.listen
  send   = each.value.send
  manage = each.value.manage

}

resource "azurerm_eventhub" "events" {
  for_each            = local.hubs
  name                = each.key
  **namespace_name      = azurerm_eventhub_namespace.eventhub.*.name  <------ problematic line**

  resource_group_name = var.resource_group
  partition_count     = each.value.partitions
  message_retention   = each.value.message_retention

}

I don't know what the issue is.

1
  • Call using array
    – again
    Feb 12, 2021 at 21:29

2 Answers 2

1

I believe you should be able to reference the namespace name like so:

namespace_name = azurerm_eventhub_namespace.eventhub[0].name

However, if the namespace_name field is a required parameter on the resources you are passing it to, you might run into other issues while you are trying to set enabled to false, as the resources depend on the output of the resource you are setting to count[0]. If you want them all to be conditional on the creation of the namespace, i would recommend continuing down your approach with the for_each over the local values.

If you need to the other resources to not be created unless the namespace is created you can create a condition on your local value like this (assuming map):

locals{
    hub = var.enable_eh ? {foo = "bar", baz = "qux"} : {}
}

The for_each will omit the resources that are empty.

0

I think you should migrate from count to for_each, here is a wider context and howto:

Terraform provides a 'for_each' iterator (aka set/map or key named list) and a 'count' iterator (aka indexed list with strict ordering) which allows you to loop over elements of a list and perform multi resource deployments. What many people fail to realize is that when using indexed list with the 'count' iterator, any change to the list ordering (like removing an item) causes replacement of all items in the list, those can be important cloud resources like databases, instances, buckets etc.

High level overview here: https://www.hashicorp.com/blog/new-terraform-tutorials-on-count-and-for-each

With ‘count’, even when one of the list items needs just reconfiguration of substantial attributes, terraform might plan to destroy and/or replace the entire list of resources, making this update a clear 'no-go' in production environments. Those resources would have better support (yet not perfect) for updating its items, if the list would have been implemented using the 'for-each' iterator, simply because the difference between a simple indexed list and a set or a map is that the item names (keys) are all guaranteed to be unique, and also do not have any particular ordering.

But many times, for various reasons, one would already implement a 'count' iterator in production and face the need to migrate it later to 'for each' iterator without destroying any item and while keeping terraform states clean and synchronized with the cloud.

here is a stable workaround for anyone facing such requirement, i think this one is not well documented yet.

terraform init -var-file=./tfvars/.tfvars -reconfigure terraform state list → to grab the count list items

Change ‘count’ instructions to ‘for-each’ instructions (example):

resource "google_container_node_pool" "nodes" {

  • count = length(var.node_pools)
  • for_each = {for i, v in var.node_pools: i => v}
  • initial_node_count = var.node_pools[count.index].initial_node_count
  • initial_node_count = each.value.initial_node_count

etc ….

Now, obviously at this point the terraform plan would show it's intention to destroy and re-create everything under that list of resources.

terraform plan -var-file=./tfvars/.tfvars → not looking good

Here is the golden transformation steps (example):

terraform state mv 'google_container_node_pool.nodes[0]' 'google_container_node_pool.nodes["0"]'

In this example we manually, yet safely, transformed one of the items from numbered (indexed) list to named item in the list, keeping the old ordering as needed (although will not be important post transformation it is important during transformation).

After doing the above on all items in the list you can initiate plan again and watch the results:

terraform plan -var-file=./tfvars/.tfvars → “infrastructure matches the configuration”

Now you can safely commit, merge and apply your new terraform infrastructure code and continue working with a new key/named list, with the ‘for-each’ iterator handling item updates much better (although - nor perfect)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.