Generate nested provider blocks only when the schema defines blocks
A dynamic block emits configuration structure, not an argument value
Terraform configuration has two different ways to express repeated data. Many provider arguments accept a list, set, or map value, which you can build with an expression. Other resource schemas define nested blocks, such as repeated ingress blocks inside a security group or repeated rule blocks inside a policy resource. A for expression creates a collection value; it does not emit nested block syntax. A dynamic block is the mechanism for generating repeated nested blocks from a collection when the provider schema requires blocks.
A dynamic block names the nested block type that should be emitted and iterates over a collection. Its content block supplies the arguments for each generated nested block. The default iterator label is the dynamic block's name; it exposes key and value for the current element. A custom iterator label can make nested loops clearer or avoid colliding with an outer each value. For a map, key identifies the entry and value contains its settings. For a set, the value is the set member.
A security group example may store allowed ingress rules in a list of objects containing protocol, port range, and CIDR. A dynamic ingress block can iterate over those objects and emit one nested ingress block per rule. The resource then has a concise, data-driven structure. The collection is still subject to variable type validation, so declare required attributes and validate port ranges and CIDR formats before planning. Dynamic generation does not make malformed inputs safe.
The schema decides whether dynamic is valid. If a resource expects an attribute called route_table_ids whose value is a list, assign a collection expression directly. A dynamic block named route_table_ids would be invalid because the provider does not define a nested block by that name. Read the resource documentation or provider schema when uncertain. This is a common source of confusing diagnostics because HCL syntax alone cannot tell whether a provider expects an attribute or block.
Dynamic blocks do not create top-level resources. They only generate nested configuration inside the parent block. To create one resource instance per item, use for_each or count on the resource. To create repeated nested blocks within each resource instance, use dynamic. These can be combined, but make the two levels of repetition obvious: outer resource identity and inner block configuration. A module with several nested loops can be difficult to review and may deserve a clearer input interface.
The collection can be filtered or transformed before it reaches the dynamic block. A local with a for expression can select only enabled rules, normalize fields, or derive an object list. Keep the transformation understandable and validate that required rules are not accidentally filtered out. If security policy depends on a specific rule, a missing value should produce a validation error or an explicit denied plan rather than silently generating no block.
Nested provider blocks can have their own nesting and cardinality rules. A dynamic block can itself contain a dynamic block where the schema permits nested blocks, but deeply nested dynamic structures quickly obscure the final configuration. Name iterators explicitly, keep the data model typed, and test representative plans. If the collection has only one or two stable entries, writing literal nested blocks may be easier to understand and safer to review.
Dynamic blocks cannot generate meta-arguments such as lifecycle or provider. Terraform processes meta-arguments early to construct the graph, before it can evaluate arbitrary expressions for generated blocks. Use literal configuration for meta-arguments and choose a module or resource boundary when lifecycle behavior truly varies. Do not try to use a dynamic block as a general-purpose code generator.
In an interview, distinguish a for expression from a dynamic block: one returns a value, the other emits repeated nested block configuration. Explain that the provider schema determines which is needed, and mention validation and readability. That captures the mechanism and the design trade-off.
Use the dynamic blocks documentation to check iterator and nesting rules. Provider schemas differ, so confirm the exact nested block name and required arguments for the resource you are configuring.
Model optional ingress rules with a typed collection
Make each generated rule reviewable and safe
Start with an input variable whose type describes the rule fields: protocol, from_port, to_port, and a list of CIDR ranges. Add validation that rejects invalid port ordering, unsupported protocol values, and empty CIDR lists where a rule would have no meaning. The exact type should reflect the provider resource schema. A dynamic ingress block can iterate the validated rule collection and assign its current object's values to nested block arguments.
Give the iterator a domain-specific name when the default would be ambiguous. If an outer resource uses for_each and the inner block also uses each, a custom iterator such as rule makes expressions readable: outer each.key identifies the security group, while rule.value describes one ingress rule. This clarity matters in plans where several resources each generate several rules. Avoid relying on implicit variable names when nested scopes are involved.
Plan output should display each generated block with its final values. Review the number of blocks, protocol, port range, and CIDR destinations. An empty collection normally emits no nested blocks; verify that this is the intended behavior. A filter that removes disabled rules can be convenient, but the input should still validate that required controls exist. For example, an empty list should not silently remove all administrative access controls if the system requires a restricted management path.
A practical test uses three rules: HTTPS from an approved public range, SSH from a corporate network, and one disabled rule that should be omitted. Verify the plan includes the first two with correct ports and excludes only the explicitly disabled one. Then test empty input, invalid ports, and malformed CIDRs. Variable validation can catch shape and simple range errors; provider validation or a security policy may be needed for deeper constraints.
Use a literal nested block when the configuration is small and stable. Dynamic generation adds indirection: a reviewer must connect the input collection to the emitted block. It is valuable when repeated blocks vary by data and the data model is also used elsewhere. If a dynamic block merely wraps one hard-coded item, it makes the resource harder to read without reducing duplication.
When the parent resource uses for_each, be careful with keys and provider block values. The outer set of resource instances needs stable keys known during planning. The inner dynamic content can use each resource's value and may contain provider attributes unknown until apply if the schema allows them. A collection that determines block count should be known enough for Terraform to construct the planned structure. Test this dependency in the actual plan rather than assuming all unknown values are accepted equally.
A dynamic block is not a security boundary. Generating ingress rules from a variable does not verify that the CIDR is safe or that another resource does not expose the same service. Add policy checks for organization-wide invariants, and avoid giving the variable an unconstrained caller if only approved ranges are allowed. The module should validate its contract and the deployment pipeline should enforce critical policy.
In a module, document whether callers pass raw provider-shaped objects or a higher-level domain model. A raw shape closely matches the provider but couples the module to provider schema. A domain model can be easier for callers but needs transformation logic and tests. Choose based on reuse and provider portability. Avoid claiming provider independence when nested block structures differ across clouds.
After changing an input type, inspect plans from every module caller. A field rename or list-to-map conversion can change generated blocks and lead to replacement of the parent resource. Version module interfaces and provide migration notes. Dynamic blocks reduce repetitive syntax, but the collection shape is still a public module contract that deserves review.
Keep generated configuration simpler than the blocks it replaces
Dynamic blocks are a tool for repetition, not abstraction by default
Use dynamic blocks when several nested blocks share a schema and are driven by real collection data. They are not intended to hide every repeated-looking line. Two static network rules may be clearer as literal blocks because a reviewer can see each destination and port directly. A long list of generated firewall entries is easier to validate and maintain as structured input. Let the size and variability of the data determine whether indirection helps.
Provider schema semantics matter. Some nested blocks use set semantics, so order does not matter and duplicates may be collapsed or rejected. Others behave like lists, where order affects the plan. Dynamic blocks emit exactly the schema-defined structure; they do not determine lifecycle semantics. Review how the provider models the nested block and whether changing one entry updates the parent in place or forces replacement. Do not use a dynamic block to obscure a large destructive diff.
Inputs should be typed and validated. A map of loosely typed values may defer errors until provider planning, where messages are harder for callers to understand. Object types make required fields explicit. Variable validation can express simple ranges and allowed values. Resource preconditions or policy checks can enforce conditions requiring more context. Keep validation close to the module interface so invalid data fails before an apply is considered.
A dynamic block cannot generate lifecycle, depends_on, provider, or other meta-arguments. These are interpreted while Terraform constructs the graph and require literal configuration. If different instances need different lifecycle behavior, separate resources or modules with explicit branches may be clearer. Avoid trying to express configuration conditions that Terraform's evaluation order does not support.
Nested dynamic blocks can become hard to reason about. Each iterator should have a unique and meaningful name, and the input should mirror the conceptual data model. If a configuration requires several levels of generated blocks, break it into modules or locals and add tests for the resulting plan. At some point, the abstraction costs more than the repetition it removes.
Use plan-based policy for controls that must hold across all callers. A module can validate its own input, but a caller may use a different module or direct resource block. A policy gate can inspect the planned result and enforce a global rule. Static scanning can catch known patterns earlier, while the plan policy sees resolved values. Keep those responsibilities separate and give violations a remediation message.
A common failure is generating zero blocks due to a filter condition that is false for every entry. Another is iterating the map key when the code expects the object value. A third is using an attribute name where the provider expects a nested block, or vice versa. To diagnose, inspect the input value, iterator key/value, generated plan, and provider schema in that order. Reduce the collection to one known item to isolate shape errors.
In an interview, explain the provider schema distinction and describe when you would keep literal blocks. Discuss input validation, plan review, and why dynamic cannot generate meta-arguments. This demonstrates you know both capability and limits.
Use for Expressions and Comprehensions when the result is an argument value or transformed collection, and The for_each Meta-argument when repetition should create separately addressed resources. Choosing among these three mechanisms keeps resource identity, nested configuration, and ordinary values distinct.
Quiz
1. When is a dynamic block appropriate?
2. What does a for expression produce compared with a dynamic block?
3. Why use a custom iterator label in nested repetition?
4. What should be validated before generating ingress blocks?
Flashcards
Question
What does a dynamic block generate?
Click to reveal answer
Answer
Repeated nested blocks whose type is defined by the provider resource schema.
Question
What does a for expression generate?
Click to reveal answer
Answer
A collection value such as a tuple or object, not nested block syntax.
Question
Why name a dynamic block iterator explicitly?
Click to reveal answer
Answer
To clarify the current item, especially when nested inside an outer for_each resource.
Question
Can a dynamic block generate lifecycle or provider meta-arguments?
Click to reveal answer
Answer
No. Meta-arguments must be declared literally because Terraform processes them while building the graph.
Question
When is a literal nested block clearer?
Click to reveal answer
Answer
When the repeated configuration is small and stable, so indirection would obscure the actual values.
Revision Notes
Key Takeaways
- 1. Dynamic blocks emit repeated nested blocks defined by the provider schema.
- 2. For expressions construct collection values; for_each creates separately addressed resource or module instances.
- 3. Typed inputs, validations, and plan review keep generated blocks understandable and safe.
- 4. Dynamic blocks cannot generate meta-arguments such as lifecycle, provider, or depends_on.
- 5. Use literal blocks when repetition is small and stable or when dynamic indirection obscures intent.
Interview Tips
- • Distinguish nested block generation from collection transformation and resource repetition.
- • Explain how iterator key and value work and why custom labels help nested scopes.
- • Mention provider schema, input validation, and review of the generated plan.
- • Name a case where literal blocks are clearer and a limitation around meta-arguments.
Cheat Sheet
dynamic Blocks for Repeatable Nesting Cheat Sheet
Phase: Dynamic Infrastructure with count, for_each, and dynamic | Level: advanced
Key points:
- dynamic blocks iterate a collection to emit repeated nested blocks at plan time.
- Each generated block exposes its current element through the iterator label.
- The content body controls which attributes each generated block receives.
- dynamic blocks only work for nested blocks, never for top-level arguments like name.
Tags: terraform, dynamic, blocks
Continue Learning
Before You Start
- No prerequisites listed.