I have following problem:
We have a module responsible for creating an Azure Event Hubs namespace along with multiple event hubs. The list of event hubs is represented as a list of objects. I want to expand this module with support for Azure Event Hubs Data Capture, however this needs to be present only for some event hubs. I started with adding a capture_description
field to the appropriate variable definition of the modules variables definition like here:
variable "event_hubs" {
description = "List of Event Hubs that will live in the namespace."
type = list(object({
name = string
message_retention = number
partition_count = number
consumer_groups = list(string)
capture_description = object({
enabled = bool
encoding = string
interval_in_seconds = number
size_limit_in_bytes = number
skip_empty_archives = bool
destination = object({
name = string
archive_name_format = string
blob_container_name = string
storage_account_id = string
})
})
}))
default = []
}
The problem is that I want the capture_description
to be optional so it can be unset for some elements of the list. Then, in main.tf
there is a foreach loop creating a resource for each element of the list from variables.tf
. I came up with a following solutioon based on "dynamic" block and foreach loop that should have 0 or 1 iterations depending on whether the capture_description
has been set. It look like this:
# Create the Event Hub(s)
resource "azurerm_eventhub" "ehub" {
for_each = local.event_hubs
name = each.value.name
partition_count = each.value.partition_count
message_retention = each.value.message_retention
namespace_name = azurerm_eventhub_namespace.namespace.name
resource_group_name = azurerm_eventhub_namespace.namespace.resource_group_name
dynamic "capture_description" {
for_each = each.capture_description == null ? [] : list(var.capture_description)
content {
enabled = capture_description.enabled
encoding = capture_description.encoding
interval_in_seconds = capture_description.interval_in_seconds
size_limit_in_bytes = capture_description.size_limit_in_bytes
skip_empty_archives = capture_description.skip_empty_archives
destination = {
name = capture_description.destination.name
archive_name_format = capture_description.destination.archive_name_format
blob_container_name = capture_description.destination.blob_container_name
storage_account_id = capture_description.destination.storage_account_id
}
}
}
}
However I have serious doubts whether it's going to work as expected. Are you able to evaluate my proposed solution and suggests something that has a non-zero chance of working? Thanks in advance!
To declare an attribute of an object type constraint as being optional you can use the optional
modifier.
variable "event_hubs" {
description = "List of Event Hubs that will live in the namespace."
type = list(object({
name = string
message_retention = number
partition_count = number
consumer_groups = list(string)
capture_description = optional(object({
enabled = bool
encoding = string
interval_in_seconds = number
size_limit_in_bytes = number
skip_empty_archives = bool
destination = object({
name = string
archive_name_format = string
blob_container_name = string
storage_account_id = string
})
}))
}))
default = []
}
The optional
modifier means that when converting an object that doesn't have that attribute Terraform will automatically insert that attribute with the default value null
.
You can use that fact in a dynamic
block by using the splat operator [*]
to concisely convert the value from a single value that might be null into a list with either zero or one elements:
dynamic "capture_description" {
for_each = each.capture_description[*]
content {
# ...
}
}
The [*]
operator will produce []
(empty sequence) if its operand is null
, or a single-element sequence containing the operand if the operand is not null. Therefore you can use capture_description.value
inside the content
block to refer to the capture_description
object passed in by the caller.