| --- |
| page_title: Dynamic Blocks - Configuration Language |
| description: >- |
| Dynamic blocks automatically construct multi-level, nested block structures. |
| Learn to configure dynamic blocks and understand their behavior. |
| --- |
| |
| # `dynamic` Blocks |
| |
| Within top-level block constructs like resources, expressions can usually be |
| used only when assigning a value to an argument using the `name = expression` |
| form. This covers many uses, but some resource types include repeatable _nested |
| blocks_ in their arguments, which typically represent separate objects that |
| are related to (or embedded within) the containing object: |
| |
| ```hcl |
| resource "aws_elastic_beanstalk_environment" "tfenvtest" { |
| name = "tf-test-name" # can use expressions here |
| |
| setting { |
| # but the "setting" block is always a literal block |
| } |
| } |
| ``` |
| |
| You can dynamically construct repeatable nested blocks like `setting` using a |
| special `dynamic` block type, which is supported inside `resource`, `data`, |
| `provider`, and `provisioner` blocks: |
| |
| ```hcl |
| resource "aws_elastic_beanstalk_environment" "tfenvtest" { |
| name = "tf-test-name" |
| application = "${aws_elastic_beanstalk_application.tftest.name}" |
| solution_stack_name = "64bit Amazon Linux 2018.03 v2.11.4 running Go 1.12.6" |
| |
| dynamic "setting" { |
| for_each = var.settings |
| content { |
| namespace = setting.value["namespace"] |
| name = setting.value["name"] |
| value = setting.value["value"] |
| } |
| } |
| } |
| ``` |
| |
| A `dynamic` block acts much like a [`for` expression](/language/expressions/for), but produces |
| nested blocks instead of a complex typed value. It iterates over a given |
| complex value, and generates a nested block for each element of that complex |
| value. |
| |
| - The label of the dynamic block (`"setting"` in the example above) specifies |
| what kind of nested block to generate. |
| - The `for_each` argument provides the complex value to iterate over. |
| - The `iterator` argument (optional) sets the name of a temporary variable |
| that represents the current element of the complex value. If omitted, the name |
| of the variable defaults to the label of the `dynamic` block (`"setting"` in |
| the example above). |
| - The `labels` argument (optional) is a list of strings that specifies the block |
| labels, in order, to use for each generated block. You can use the temporary |
| iterator variable in this value. |
| - The nested `content` block defines the body of each generated block. You can |
| use the temporary iterator variable inside this block. |
| |
| Since the `for_each` argument accepts any collection or structural value, |
| you can use a `for` expression or splat expression to transform an existing |
| collection. |
| |
| The iterator object (`setting` in the example above) has two attributes: |
| |
| - `key` is the map key or list element index for the current element. If the |
| `for_each` expression produces a _set_ value then `key` is identical to |
| `value` and should not be used. |
| - `value` is the value of the current element. |
| |
| A `dynamic` block can only generate arguments that belong to the resource type, |
| data source, provider or provisioner being configured. It is _not_ possible |
| to generate meta-argument blocks such as `lifecycle` and `provisioner` |
| blocks, since Terraform must process these before it is safe to evaluate |
| expressions. |
| |
| The `for_each` value must be a collection with one element per desired |
| nested block. If you need to declare resource instances based on a nested |
| data structure or combinations of elements from multiple data structures you |
| can use Terraform expressions and functions to derive a suitable value. |
| For some common examples of such situations, see the |
| [`flatten`](/language/functions/flatten) |
| and |
| [`setproduct`](/language/functions/setproduct) |
| functions. |
| |
| ## Multi-level Nested Block Structures |
| |
| Some providers define resource types that include multiple levels of blocks |
| nested inside one another. You can generate these nested structures dynamically |
| when necessary by nesting `dynamic` blocks in the `content` portion of other |
| `dynamic` blocks. |
| |
| For example, a module might accept a complex data structure like the following: |
| |
| ```hcl |
| variable "load_balancer_origin_groups" { |
| type = map(object({ |
| origins = set(object({ |
| hostname = string |
| })) |
| })) |
| } |
| ``` |
| |
| If you were defining a resource whose type expects a block for each origin |
| group and then nested blocks for each origin within a group, you could ask |
| Terraform to generate that dynamically using the following nested `dynamic` |
| blocks: |
| |
| ```hcl |
| dynamic "origin_group" { |
| for_each = var.load_balancer_origin_groups |
| content { |
| name = origin_group.key |
| |
| dynamic "origin" { |
| for_each = origin_group.value.origins |
| content { |
| hostname = origin.value.hostname |
| } |
| } |
| } |
| } |
| ``` |
| |
| When using nested `dynamic` blocks it's particularly important to pay attention |
| to the iterator symbol for each block. In the above example, |
| `origin_group.value` refers to the current element of the outer block, while |
| `origin.value` refers to the current element of the inner block. |
| |
| If a particular resource type defines nested blocks that have the same type |
| name as one of their parents, you can use the `iterator` argument in each of |
| `dynamic` blocks to choose a different iterator symbol that makes the two |
| easier to distinguish. |
| |
| ## Best Practices for `dynamic` Blocks |
| |
| Overuse of `dynamic` blocks can make configuration hard to read and maintain, so |
| we recommend using them only when you need to hide details in order to build a |
| clean user interface for a re-usable module. Always write nested blocks out |
| literally where possible. |
| |
| If you find yourself defining most or all of a `resource` block's arguments and |
| nested blocks using directly-corresponding attributes from an input variable |
| then that might suggest that your module is not creating a useful abstraction. |
| It may be better for the calling module to define the resource itself then |
| pass information about it into your module. For more information on this design |
| tradeoff, see [When to Write a Module](/language/modules/develop#when-to-write-a-module) |
| and [Module Composition](/language/modules/develop/composition). |