Dynamic syntax use count(if condition)

Hello.
I have a question using dynamic syntax with if condition.
Now, our terraform version is v0.12.29.

I write this in terraform file.

dynamic "database_flags" {
      count = var.database_version == "MYSQL_5_7" ? 1 : 0
      content {
        name  = "lower_case_table_names"
        value = 1
      }
    }

When I run terraform plan command, I see this syntax error.
An argument named “count” is not expected here.

I think we can’t use count in dynamic syntax.
So I try to for_each in dynamic syntax.

dynamic "database_flags" {
      for_each = var.database_version == "MYSQL_5_7" ? [1] : [0]
      content {
        name  = "lower_case_table_names"
        value = 1
      }
    }

Is this same meaning?

count = var.database_version == "MYSQL_5_7" ? 1 : 0

for_each = var.database_version == "MYSQL_5_7" ? [1] : []

Hi @Danpatpang,

At the level of whole resources, Terraform distinguishes count and for_each because they each cause Terraform to track the individual instances differently: count tracks instances by integers counting up from zero, while for_each tracks the instances by strings.

dynamic blocks are different in that they don’t require Terraform to do any special tracking of each individual block. Instead, it’s just as if you’d written multiple blocks out manually yourself, with the provider unable to tell the difference.

For that reason, for_each inside a dynamic block can work with any kind of collection, including lists as you’ve shown here. However, if you want to generate a different number of blocks in different cases then it’s important to ensure that your for_each collection has a different number of elements in each case, which isn’t true in the first example you shared because [1] and [0] both have one element, and so will generate only one block.


Since this database_flags block type seems to be representing a key/value pair, where multiple such blocks presumably represent multiple key/value pairs, you might find it easier to read and maintain if you factor out the database version condition into a more convenient data structure and then use that data structure in the dynamic block.

For example, we can declare a map of maps that gives the “flags” for each database_version as a Local Value:

locals {
  database_flags = tomap({
    MYSQL_5_7 = tomap({
      # Note that the value argument in database_flags
      # actually expects a string, so using a string here
      # avoids type ambiguity that might cause Terraform
      # to produce a type error here.
      lower_case_table_names = "1"
    })
  })
}

Then your dynamic block can be based on whichever sub-map is appropriate for the selected database version:

  dynamic "database_flags" {
    for_each = try(local.database_flags[var.database_version], tomap({}))
    content {
      name  = database_flags.key
      value = database_flags.value
    }
  }

This uses try to avoid generating an error if the selected database version has no entry in the local.database_flags map. If there is such an element then it’ll use the elements of the nested map to generate zero or more database_flags blocks. If you need to add, remove, or change flags in the future you can do so in the local value data structure and leave the dynamic block unchanged.