Labels in dynamic block

Hi, first question.

How do we use labels in (Dynamic Blocks - Configuration Language - Terraform by HashiCorp)

Terraform version

Terraform v0.13.2
+ provider registry.terraform.io/hashicorp/aws v3.29.1

I was assuming that I can use it this way


dynamic "bla" {
    for_each = local.bla
    labels = ["foo", "bar"]
    content { 
        some_key = bla.values
}
}

Assuming that bla is the correct type of block and I am not running into
Blocks of type "bla" are not expected here

I was expecting to get below when I run:

foo {
some_key = bla.values
} 

bar {
some_key = bla.values
}

But when I am tried planning, I get
An argument named "labels" is not expected here.

Is labels not meant to be used that way in dynamic block ?

Context:
I am trying to allow condition in aws_lb_listener_rule to have dynamic block for its condition.

locals {
condition = {
"host_header" = { 
values = [var.bla] 
} 
"path_pattern" = {
values = [var.foo]
}
}
} 

resource "aws_lb_listener_rule" "https" {
...

  dynamic "condition" {
    for_each = local.condition
    content {
        dynamic "host_header" {
        for_each = local.condition
        labels = ["host_header", "path_header"]
        content {
          values = condition.values.values
         }
      }
    }
  }

Expected output would be:

resource "aws_lb_listener_rule" "https" {
...

condition {
host_header {
values = []
} 

condition {
path_pattern {
values = []
} 
}

I’m not 100% sure I’m understanding the question, but if you are wanting the equivalent of the code at the bottom of your post while using the local variable given then something like this would work:

resource "aws_lb_listener_rule" "https" {
...

  dynamic "condition" {
    for_each = lookup(local.condition, "host_header", {})

    content {
      host_header {
        values = condition.values.values
      }
    }
  }

  dynamic "condition" {
    for_each = lookup(local.condition, "path_pattern", {})

    content {
      path_pattern {
        values = condition.values.values
      }
    }
  }
}
1 Like

Hi @keatmin,

I think dynamic labels isn’t the right solution to your current problem, because it’s for creating blocks that have label strings after the block type. In other words, the example you showed with aws_lb_listener_rule would (if I’m reading your partial example correctly) generate something like the following structure:

resource "aws_lb_listener_rule" "https" {

  condition {
    host_header "host_header" "path_header" {
      values = ...
    }
  }
}

Guessing a bit from the particular label values you used here, and referring to the documentation for this resource type, I think your intent is to generate separate host_header and path_header blocks based on the input. However, your condition local value is just a single object rather than a list, and likewise you only have one host_header object and one path_pattern object in there, so it doesn’t seem like you need dynamic blocks at all here:

resource "aws_lb_listener_rule" "https" {

  condition {
    host_header {
      values = local.condition.host_header.values
    }
    path_pattern {
      values = local.condition.path_pattern.values
    }
  }
}

You only need to use dynamic if the number of blocks to be included is dynamically-decided; if you only need one block of each type then you can just write them out, like I did above.

2 Likes

Hi,

On the dynamic labels feature, I am confused with what it does and can’t find an example or valid use case. The docs (Dynamic Blocks - Configuration Language - Terraform by HashiCorp) does not make it clearer to me. Would you mind sharing a practical example of its usage?

3 Likes

A dynamic block with labels would be useful only for a resource type whose schema includes a block type that expects labels. There aren’t many examples of that, so in practice labels doesn’t see much use.

Block types with labels are so rare in providers that I had to think a bunch to remember one to use as an example! And my example happens to be a community provider I published as part of experimenting with testing in Terraform, which has a data source called testing_assertions which has a block type equal which expects one label to uniquely name each of the assertions.

Here’s how it looks without dynamic:

data "testing_assertions" "terraform_disco" {
  subject = "Terraform discovery document"

  equal "contents" {
    statement = "has the expected content"

    got  = jsondecode(data.http.terraform_disco.body)
    want = {
      "modules.v1": "${module.mut.base_url}/modules/v1"
    }
  }
  equal "content_type" {
    statement = "has JSON content type"

    got  = data.http.terraform_disco.response_headers["content-type"]
    want = "application/json"
  }
}

Now I want to reinforce that I’m only using this resource type as an example because it was the one I thought of while trying to remember an instance of a block type expecting a label, and I don’t think this would actually be a useful thing to do in practice, but here’s how you could in principle write the above equal blocks using dynamic:

locals {
  test_assertions = {
    contents = {
      statement = "has the expected content"
      got       = jsondecode(data.http.terraform_disco.body)
      want = {
        "modules.v1": "${module.mut.base_url}/modules/v1"
      }
    }
    content_type = {
      statement = "has JSON content type"
      got       = data.http.terraform_disco.response_headers["content-type"]
      want      = "application/json"
    }
  }
}

data "testing_assertions" "terraform_disco" {
  subject = "Terraform discovery document"

  dynamic "equal" {
    for_each = local.test_assertions
    labels   = [each.key]
    content {
      statement = each.value.content
      got       = each.value.got
      want      = each.value.want
    }
  }
}
2 Likes

Ah, cheers @apparentlymart

Thanks for explaining how to use the label.

The goal that I was trying to achieve with the dynamic block was two condition block with either a host_header or path_pattern block in the condition block depending on what’s in the local and var as such:

condition {
  host_header {
    values = []
  }
}

condition {
  path_pattern {
    values = []
  }
}

I was thinking that having the label there would allow me to dynamically generate
host_header and path_pattern for the dynamic condition.

I am impressed that you still get what I was trying to achieve despite how confusing my question was!

Yup, this is one of the solution that I went with seeing that the labels part is not how I assumed it should work HAHA.

I was wondering if there was a way to only use one nested dynamic block like how I described and ended up here trying to avoid writing ONE more dynamic block.

For people who are confused with my question.

My goal is to achieve:

resource "aws_lb_listener_rule" "https" {
...

condition {
host_header {
values = []
} 

condition {
path_pattern {
values = []
} 
}

I thought that I could achieve it with labels in the dynamic block to override the dynamic host_header with either host_header or path_pattern in labels:

resource "aws_lb_listener_rule" "https" {
...

  dynamic "condition" {
    for_each = local.condition
    content {
        dynamic "host_header" {
        for_each = local.condition
        labels = ["host_header", "path_header"]
        content {
          values = condition.values.values
         }
      }
    }
  }

The suggestion from @stuart-c is how I will go about doing it.

How can I dynamically construct the authentication block based on the input?

Resource of interest: google_iam_workload_identity_pool_provider

resource "google_iam_workload_identity_pool_provider" "example" {
  workload_identity_pool_id          = google_iam_workload_identity_pool.pool.workload_identity_pool_id
  workload_identity_pool_provider_id = "example-prvdr"
  attribute_mapping                  = {
    "google.subject" = "assertion.sub"
  }
  oidc {
    issuer_uri        = "https://sts.windows.net/azure-tenant-id"
  }
}

Here I cannot use labels as it accepts list, but in this case there can be only 1 authentication block (aws, saml or oidc ) per resource, kindly suggest.

FWIW: was looking into how to use lables and stumbled upon this thread and it looks like Martin’s example should have all occurrences of each replaced with equal to match dynamic block label name (e.g. each.key should be equal.key, each.value.content should be equal.value.content, so on).