Terraform dynamic blocks require a 'content' block to define the attributes of generated nested blocks. This error occurs when the content block is missing, incorrectly named, or when you try to pass data directly without the required content wrapper.
Terraform dynamic blocks are a way to generate repeated nested blocks programmatically using a loop. The dynamic block syntax requires three key parts: the dynamic label, a for_each iterator, and a content block that defines the attributes. When Terraform parses your configuration, it expects each dynamic block to contain a 'content' block that specifies what nested block attributes should be generated. This error means Terraform couldn't find the required content block in your dynamic block declaration.
First, review the correct syntax for dynamic blocks in Terraform. A dynamic block must have this structure:
dynamic "block_name" {
for_each = var.items
content {
# attributes go here
key = block_name.value.some_attribute
}
}Note the three essential parts:
1. dynamic "block_name" - declares a dynamic block for a specific nested block type
2. for_each - provides the collection to iterate over
3. content - required block that defines what attributes each generated block should have
Open your Terraform configuration file and locate the dynamic block mentioned in the error. Verify it contains a content block:
# WRONG - missing content block
dynamic "ingress" {
for_each = var.ingress_rules
{
from_port = ingress.value.from_port
}
}
# CORRECT - with content block
dynamic "ingress" {
for_each = var.ingress_rules
content {
from_port = ingress.value.from_port
}
}Ensure the keyword is spelled exactly as content (not contents, config, block, or any other variation):
# WRONG - misspelled
dynamic "tag" {
for_each = var.tags
contents { # <-- Wrong spelling
key = tag.value.key
value = tag.value.value
}
}
# CORRECT - proper spelling
dynamic "tag" {
for_each = var.tags
content { # <-- Correct spelling
key = tag.value.key
value = tag.value.value
}
}Inside the content block, you must use iterator_name.value to access attributes from each item in the for_each collection:
# WRONG - missing .value
dynamic "security_group_rule" {
for_each = var.rules
content {
type = rules.protocol # Missing .value
from_port = rules.from_port # Missing .value
}
}
# CORRECT - using .value
dynamic "security_group_rule" {
for_each = var.rules
content {
type = security_group_rule.value.protocol
from_port = security_group_rule.value.from_port
}
}Note: The iterator name defaults to the block name (e.g., security_group_rule in this case), but you can customize it with the optional iterator argument.
Ensure proper HCL indentation so Terraform recognizes the content block as part of the dynamic block:
# WRONG - content block not properly nested
dynamic "listener" {
for_each = var.listeners
ontent { # <-- Missing indent makes this appear as new block
port = listener.value.port
}
}
# CORRECT - content block properly indented
dynamic "listener" {
for_each = var.listeners
content {
port = listener.value.port
}
}After making corrections, validate your Terraform configuration:
terraform validateIf validation passes, the error should be resolved. If you still see the error, review the error message line number and re-examine the dynamic block at that location.
For nested dynamic blocks or when you want clearer code, use the iterator argument to rename the loop variable:
dynamic "ingress" {
for_each = var.ingress_rules
iterator = rule # Rename 'ingress' to 'rule' for clarity
content {
from_port = rule.value.from_port
to_port = rule.value.to_port
}
}This is especially helpful when you have nested dynamic blocks with the same name, as it removes ambiguity about which iterator you're referencing.
Attributes as Blocks Mode: Some Terraform providers use a special 'attributes as blocks' mode for backward compatibility. In this mode, certain attributes can be written as nested blocks instead of key-value pairs. Dynamic blocks do NOT work with attributes in this mode because the configuration schema becomes ambiguous. If you encounter this issue, you must write those blocks statically instead of using dynamic blocks.
Nested Dynamic Blocks: When nesting dynamic blocks, each level needs its own content block. The iterator from an outer dynamic block is not automatically available in an inner dynamic block unless you explicitly pass it through the content block:
dynamic "outer" {
for_each = var.outer_items
content {
dynamic "inner" {
for_each = outer.value.inner_items
content {
value = inner.value.name
}
}
}
}Performance Consideration: While dynamic blocks are powerful for DRY configurations, overusing them can make your configuration harder to read and maintain. HashiCorp recommends using them primarily in reusable modules where they help create a clean user interface, rather than in root modules.
Error: Error rendering template: template not found
How to fix "template not found" error in Terraform
Error: Error generating private key
How to fix 'Error generating private key' in Terraform
Error creating Kubernetes Service: field is immutable
How to fix "field is immutable" errors in Terraform
Error: Error creating local file: open: permission denied
How to fix "Error creating local file: permission denied" in Terraform
Error: line endings have changed from CRLF to LF
Line endings have changed from CRLF to LF in Terraform