Bird
Raised Fist0
Terraformcloud~7 mins

Dynamic blocks vs for_each decision in Terraform - CLI Comparison

Choose your learning style10 modes available
Introduction
When writing Terraform code, you often need to create multiple similar resources or nested blocks. Dynamic blocks and for_each loops help automate this, but choosing the right one makes your code simpler and easier to manage.
When you want to create multiple nested blocks inside a resource based on a list or map.
When you need to create multiple separate resources or modules from a collection of items.
When your nested blocks have complex structures that vary per item.
When you want to avoid repeating similar blocks manually in your configuration.
When you want to control resource creation with keys or indexes for better tracking.
Config File - main.tf
main.tf
variable "security_groups" {
  type = list(object({
    name        = string
    description = string
    ingress     = list(object({
      from_port   = number
      to_port     = number
      protocol    = string
      cidr_blocks = list(string)
    }))
  }))
  default = [
    {
      name        = "web-sg"
      description = "Allow web traffic"
      ingress = [
        {
          from_port   = 80
          to_port     = 80
          protocol    = "tcp"
          cidr_blocks = ["0.0.0.0/0"]
        }
      ]
    },
    {
      name        = "ssh-sg"
      description = "Allow SSH"
      ingress = [
        {
          from_port   = 22
          to_port     = 22
          protocol    = "tcp"
          cidr_blocks = ["192.168.1.0/24"]
        }
      ]
    }
  ]
}

resource "aws_security_group" "example" {
  for_each = { for sg in var.security_groups : sg.name => sg }

  name        = each.value.name
  description = each.value.description

  dynamic "ingress" {
    for_each = each.value.ingress
    content {
      from_port   = ingress.value.from_port
      to_port     = ingress.value.to_port
      protocol    = ingress.value.protocol
      cidr_blocks = ingress.value.cidr_blocks
    }
  }
}

This Terraform file defines a variable with a list of security groups, each having ingress rules.

The aws_security_group resource uses for_each to create one security group per item in the list, keyed by the group's name.

Inside the resource, a dynamic block creates multiple ingress blocks based on the ingress rules for each security group.

This combination shows when to use for_each for resources and dynamic blocks for nested blocks.

Commands
Initializes Terraform in the current directory, downloading required providers and preparing the environment.
Terminal
terraform init
Expected OutputExpected
Initializing the backend... Initializing provider plugins... - Finding latest version of hashicorp/aws... - Installing hashicorp/aws v4.0.0... - Installed hashicorp/aws v4.0.0 (signed by HashiCorp) Terraform has been successfully initialized!
Shows the execution plan, detailing what resources Terraform will create based on the configuration.
Terminal
terraform plan
Expected OutputExpected
An execution plan has been generated and is shown below. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # aws_security_group.example["ssh-sg"] will be created + resource "aws_security_group" "example" { + description = "Allow SSH" + name = "ssh-sg" + ... + ingress { + cidr_blocks = ["192.168.1.0/24"] + from_port = 22 + protocol = "tcp" + to_port = 22 } } # aws_security_group.example["web-sg"] will be created + resource "aws_security_group" "example" { + description = "Allow web traffic" + name = "web-sg" + ... + ingress { + cidr_blocks = ["0.0.0.0/0"] + from_port = 80 + protocol = "tcp" + to_port = 80 } } Plan: 2 to add, 0 to change, 0 to destroy.
Applies the planned changes, creating the security groups and their ingress rules automatically without asking for confirmation.
Terminal
terraform apply -auto-approve
Expected OutputExpected
aws_security_group.example["ssh-sg"]: Creating... aws_security_group.example["web-sg"]: Creating... aws_security_group.example["ssh-sg"]: Creation complete after 2s [id=sg-0123456789abcdef0] aws_security_group.example["web-sg"]: Creation complete after 2s [id=sg-0fedcba9876543210] Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
→
-auto-approve - Skip interactive approval prompt
Displays the current state of the deployed infrastructure, confirming the security groups and their ingress rules exist.
Terminal
terraform show
Expected OutputExpected
... # aws_security_group.example["ssh-sg"]: resource "aws_security_group" "example" { id = "sg-0123456789abcdef0" name = "ssh-sg" description = "Allow SSH" ingress { from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = ["192.168.1.0/24"] } } # aws_security_group.example["web-sg"]: resource "aws_security_group" "example" { id = "sg-0fedcba9876543210" name = "web-sg" description = "Allow web traffic" ingress { from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } }
Key Concept

Use for_each to create multiple resources from a collection and dynamic blocks to generate multiple nested blocks inside a resource based on complex data.

Common Mistakes
Using for_each inside a resource to create nested blocks instead of dynamic blocks
Terraform does not support for_each directly on nested blocks; it causes syntax errors.
Use dynamic blocks with for_each inside the resource to generate nested blocks.
Using dynamic blocks to create multiple resources instead of nested blocks
Dynamic blocks only work inside a resource for nested blocks, not for creating separate resources.
Use for_each at the resource level to create multiple resources.
Not keying for_each with unique keys when creating multiple resources
Terraform requires unique keys for for_each to track resources properly; using a list without keys can cause issues.
Convert the list to a map with unique keys, such as resource names, before using for_each.
Summary
Use for_each on resources to create multiple instances from a map or set of unique keys.
Use dynamic blocks inside resources to generate multiple nested blocks based on complex data structures.
Combining for_each and dynamic blocks helps write clean, reusable Terraform code for complex infrastructure.

Practice

(1/5)
1. Which Terraform feature should you use to repeat a nested block inside a single resource?
easy
A. Dynamic blocks
B. for_each on the resource
C. count on the resource
D. locals block

Solution

  1. Step 1: Understand nested block repetition

    Dynamic blocks are designed to repeat nested blocks inside a single resource.
  2. Step 2: Compare with for_each usage

    for_each repeats entire resources or top-level blocks, not nested blocks.
  3. Final Answer:

    Dynamic blocks -> Option A
  4. Quick Check:

    Nested block repetition = Dynamic blocks [OK]
Hint: Use dynamic blocks for nested repeated settings [OK]
Common Mistakes:
  • Using for_each to repeat nested blocks inside one resource
  • Confusing count with dynamic blocks
  • Trying to use locals for repeating blocks
2. Which syntax correctly uses a dynamic block inside a resource to repeat a nested block named setting?
easy
A. locals { setting = var.settings } resource { setting = local.setting }
B. for_each = var.settings { setting { key = each.value } }
C. count = length(var.settings) setting { key = var.settings[count.index] }
D. dynamic "setting" { for_each = var.settings; content { key = each.value } }

Solution

  1. Step 1: Identify correct dynamic block syntax

    Dynamic blocks use the syntax: dynamic "block_name" { for_each = ... content { ... } }.
  2. Step 2: Check other options for syntax errors

    Options A, B, and C misuse for_each, count, or locals incorrectly for nested blocks.
  3. Final Answer:

    dynamic "setting" { for_each = var.settings; content { key = each.value } } -> Option D
  4. Quick Check:

    Correct dynamic block syntax = dynamic "setting" { for_each = var.settings; content { key = each.value } } [OK]
Hint: Dynamic blocks use dynamic "name" with for_each and content [OK]
Common Mistakes:
  • Placing for_each directly inside resource without dynamic
  • Using count for nested blocks
  • Trying to assign locals directly as blocks
3. Given this Terraform snippet, what will be the number of aws_security_group_rule resources created?
resource "aws_security_group_rule" "example" {
  for_each = { for idx, port in var.ports : idx => port }
  type        = "ingress"
  from_port   = each.value
  to_port     = each.value
  protocol    = "tcp"
  security_group_id = var.sg_id
}
Assuming var.ports = [80, 443].
medium
A. 1 resource
B. 2 resources
C. No resources, syntax error
D. One resource with two ports

Solution

  1. Step 1: Understand for_each on resource

    for_each creates one resource instance per map or set element. Here, var.ports has 2 elements.
  2. Step 2: Count resource instances

    Since var.ports has 2 ports, Terraform creates 2 aws_security_group_rule resources, one for each port.
  3. Final Answer:

    2 resources -> Option B
  4. Quick Check:

    for_each with 2 items = 2 resources [OK]
Hint: for_each creates one resource per item in collection [OK]
Common Mistakes:
  • Thinking for_each creates one resource regardless of items
  • Confusing dynamic blocks with resource repetition
  • Assuming ports combine into one resource
4. This Terraform code throws an error. What is the main issue?
resource "aws_instance" "example" {
  for_each = var.instances
  ami           = "ami-123456"
  instance_type = "t2.micro"

  dynamic "ebs_block_device" {
    for_each = each.value.ebs_volumes
    content {
      device_name = ebs_block_device.value.device_name
      volume_size = ebs_block_device.value.size
    }
  }
}
Assuming var.instances is a map with nested ebs_volumes lists.
medium
A. Dynamic blocks cannot be nested inside resources
B. for_each cannot be used on resources
C. Incorrect reference to dynamic block variable inside content
D. Missing count argument on resource

Solution

  1. Step 1: Identify the error in dynamic block reference

    Inside the content block of a dynamic block, the iterator must be referenced as each.value, not ebs_block_device.value. The iterator variable is always each regardless of the dynamic block name.
  2. Step 2: Confirm the fix

    Change to device_name = each.value.device_name and volume_size = each.value.size. Other options are incorrect: dynamic blocks work inside resources, for_each works on resources, count is not required.
  3. Final Answer:

    Incorrect reference to dynamic block variable inside content -> Option C
  4. Quick Check:

    Dynamic block content uses each.value [OK]
Hint: Inside dynamic content, always use each.value, not block_name.value [OK]
Common Mistakes:
  • Using block_name.value inside dynamic block content instead of each.value
  • Trying to use for_each on resource incorrectly
  • Forgetting to define for_each on dynamic block
5. You want to create multiple AWS IAM users, each with multiple inline policies. Which Terraform approach is best to manage this efficiently? A snippet of your variables:
variable "users" {
  type = map(object({
    policies = list(object({
      name   = string
      policy = string
    }))
  }))
}
hard
A. Use for_each on aws_iam_user for users, and for_each on aws_iam_user_policy for policies
B. Use count on aws_iam_user for users, and for_each on aws_iam_user_policy for policies
C. Use for_each on aws_iam_user resource for users, and dynamic blocks inside aws_iam_user_policy for policies
D. Use dynamic blocks on aws_iam_user resource for users, and count on aws_iam_user_policy for policies

Solution

  1. Step 1: Manage multiple users

    Use for_each on aws_iam_user resource to create one user per map entry.
  2. Step 2: Manage multiple policies per user

    Since policies are separate resources (aws_iam_user_policy), use for_each on that resource to create one policy per user-policy pair.
  3. Step 3: Why not dynamic blocks here?

    Dynamic blocks repeat nested blocks inside a resource, but aws_iam_user_policy is a separate resource, so for_each is appropriate.
  4. Final Answer:

    Use for_each on aws_iam_user for users, and for_each on aws_iam_user_policy for policies -> Option A
  5. Quick Check:

    Multiple resources = for_each on both [OK]
Hint: Use for_each for multiple resources, dynamic blocks for nested blocks [OK]
Common Mistakes:
  • Using dynamic blocks to create multiple resources
  • Using count instead of for_each for maps
  • Trying to nest policies inside user resource with dynamic blocks
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard