Bird
Raised Fist0
Terraformcloud~7 mins

Dynamic blocks in security groups in Terraform - Commands & Configuration

Choose your learning style10 modes available
Introduction
When you want to create security groups with multiple similar rules, writing each rule manually can be repetitive and error-prone. Dynamic blocks in Terraform let you generate multiple security group rules from a list, making your configuration cleaner and easier to manage.
When you need to add several inbound or outbound rules to a security group based on a list of ports or CIDR blocks.
When you want to avoid repeating similar blocks in your Terraform files to keep them concise.
When your security group rules depend on variable input that can change in number or values.
When you want to maintain your infrastructure code easily as rules grow or shrink.
When you want to programmatically generate rules without manually duplicating code.
Config File - main.tf
main.tf
provider "aws" {
  region = "us-east-1"
}

variable "allowed_ports" {
  type    = list(number)
  default = [22, 80, 443]
}

resource "aws_security_group" "example" {
  name        = "example-sg"
  description = "Security group with dynamic rules"
  vpc_id      = "vpc-12345678"

  dynamic "ingress" {
    for_each = var.allowed_ports
    content {
      from_port   = ingress.value
      to_port     = ingress.value
      protocol    = "tcp"
      cidr_blocks = ["0.0.0.0/0"]
    }
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

This Terraform file creates an AWS security group with dynamic ingress rules.

  • variable "allowed_ports" defines a list of ports to allow inbound traffic.
  • dynamic "ingress" block loops over each port in allowed_ports and creates an ingress rule for it.
  • Each ingress rule allows TCP traffic on the specified port from anywhere.
  • The egress block allows all outbound traffic.
  • Replace vpc-12345678 with your actual VPC ID.
Commands
Initializes the Terraform working directory and downloads the AWS provider plugin.
Terminal
terraform init
Expected OutputExpected
Initializing the backend... Initializing provider plugins... - Finding latest version of hashicorp/aws... - Installing hashicorp/aws v4.54.0... - Installed hashicorp/aws v4.54.0 (signed by HashiCorp) Terraform has been successfully initialized! You may now begin working with Terraform. Try running "terraform plan" to see any changes that are required for your infrastructure.
Shows the execution plan, what Terraform will create or change 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 will be created + resource "aws_security_group" "example" { + arn = (known after apply) + description = "Security group with dynamic rules" + id = (known after apply) + name = "example-sg" + owner_id = (known after apply) + revoke_rules_on_delete = false + vpc_id = "vpc-12345678" + egress { + cidr_blocks = [ + "0.0.0.0/0", ] + from_port = 0 + protocol = "-1" + to_port = 0 } + ingress { + cidr_blocks = [ + "0.0.0.0/0", ] + from_port = 22 + protocol = "tcp" + to_port = 22 } + ingress { + cidr_blocks = [ + "0.0.0.0/0", ] + from_port = 80 + protocol = "tcp" + to_port = 80 } + ingress { + cidr_blocks = [ + "0.0.0.0/0", ] + from_port = 443 + protocol = "tcp" + to_port = 443 } } Plan: 1 to add, 0 to change, 0 to destroy.
Applies the planned changes to create the security group with dynamic rules without asking for confirmation.
Terminal
terraform apply -auto-approve
Expected OutputExpected
aws_security_group.example: Creating... aws_security_group.example: Creation complete after 3s [id=sg-0a1b2c3d4e5f6g7h8] Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
→
-auto-approve - Skips manual approval prompt to apply changes immediately
Verifies the security group and its rules were created correctly by querying AWS directly.
Terminal
aws ec2 describe-security-groups --group-ids sg-0a1b2c3d4e5f6g7h8
Expected OutputExpected
{ "SecurityGroups": [ { "GroupId": "sg-0a1b2c3d4e5f6g7h8", "GroupName": "example-sg", "Description": "Security group with dynamic rules", "IpPermissions": [ { "IpProtocol": "tcp", "FromPort": 22, "ToPort": 22, "IpRanges": [ { "CidrIp": "0.0.0.0/0" } ] }, { "IpProtocol": "tcp", "FromPort": 80, "ToPort": 80, "IpRanges": [ { "CidrIp": "0.0.0.0/0" } ] }, { "IpProtocol": "tcp", "FromPort": 443, "ToPort": 443, "IpRanges": [ { "CidrIp": "0.0.0.0/0" } ] } ], "IpPermissionsEgress": [ { "IpProtocol": "-1", "IpRanges": [ { "CidrIp": "0.0.0.0/0" } ] } ], "VpcId": "vpc-12345678" } ] }
Key Concept

If you remember nothing else from this pattern, remember: dynamic blocks let you create multiple similar resource blocks from a list, reducing repetition and making your Terraform code cleaner.

Common Mistakes
Using a static block for each rule instead of a dynamic block when rules come from a list.
This causes repetitive code that is hard to maintain and update when the list changes.
Use a dynamic block with for_each to loop over the list and generate rules automatically.
Not using ingress.value inside the dynamic block content and instead using a fixed port number.
This ignores the dynamic input and creates identical rules, defeating the purpose of dynamic blocks.
Use ingress.value (or the iterator name) to reference each item in the list inside the dynamic block.
Forgetting to specify the protocol or cidr_blocks inside the dynamic block content.
Missing required fields causes Terraform to fail or create incomplete security group rules.
Always include all required fields like protocol, from_port, to_port, and cidr_blocks inside the dynamic block content.
Summary
Define a list variable with ports you want to allow in the security group.
Use a dynamic block in the aws_security_group resource to loop over the list and create ingress rules.
Run terraform init, plan, and apply to create the security group with all rules automatically.
Verify the security group rules using AWS CLI to ensure they match the dynamic configuration.

Practice

(1/5)
1. What is the main purpose of using dynamic blocks in Terraform security groups?
easy
A. To delete all security group rules at once
B. To manually write each security group rule one by one
C. To encrypt security group rules for security
D. To create multiple similar security group rules from a list automatically

Solution

  1. Step 1: Understand the role of dynamic blocks

    Dynamic blocks allow repeating a block of code multiple times based on a list or map input.
  2. Step 2: Apply this to security groups

    In security groups, dynamic blocks help create many similar rules without writing each rule manually.
  3. Final Answer:

    To create multiple similar security group rules from a list automatically -> Option D
  4. Quick Check:

    Dynamic blocks automate repeated blocks [OK]
Hint: Dynamic blocks repeat code for lists automatically [OK]
Common Mistakes:
  • Thinking dynamic blocks delete rules
  • Believing dynamic blocks encrypt rules
  • Confusing dynamic blocks with manual rule writing
2. Which of the following is the correct syntax to start a dynamic block named ingress in a Terraform security group resource?
easy
A. dynamic ingress {
B. dynamic "ingress" {
C. dynamic ingress() {
D. dynamic "ingress"() {

Solution

  1. Step 1: Recall Terraform dynamic block syntax

    Dynamic blocks use the keyword dynamic followed by the block name in quotes and curly braces.
  2. Step 2: Match syntax for ingress block

    The correct syntax is dynamic "ingress" { to define a dynamic ingress block.
  3. Final Answer:

    dynamic "ingress" { -> Option B
  4. Quick Check:

    Dynamic block name must be in quotes [OK]
Hint: Use quotes around block name in dynamic block [OK]
Common Mistakes:
  • Omitting quotes around block name
  • Adding parentheses after block name
  • Using incorrect keywords
3. Given the following Terraform snippet, what will be the number of ingress rules created?
variable "ports" {
  default = [22, 80, 443]
}

resource "aws_security_group" "example" {
  name = "example-sg"

  dynamic "ingress" {
    for_each = var.ports
    content {
      from_port   = ingress.value
      to_port     = ingress.value
      protocol    = "tcp"
      cidr_blocks = ["0.0.0.0/0"]
    }
  }
}
medium
A. 3 ingress rules, one for each port
B. 1 ingress rule with ports 22, 80, and 443 combined
C. No ingress rules created due to syntax error
D. Ingress rules created only for port 22

Solution

  1. Step 1: Analyze the for_each expression

    The dynamic block uses for_each = var.ports where ports are [22, 80, 443]. This means it loops 3 times.
  2. Step 2: Understand content block creation

    Each iteration creates one ingress rule with from_port and to_port set to the current port value.
  3. Final Answer:

    3 ingress rules, one for each port -> Option A
  4. Quick Check:

    One rule per list item = 3 rules [OK]
Hint: Count list items for number of dynamic blocks created [OK]
Common Mistakes:
  • Thinking all ports combine into one rule
  • Assuming syntax error without checking
  • Ignoring for_each iteration count
4. Identify the error in this Terraform dynamic block for security group ingress rules:
dynamic "ingress" {
  for_each = var.rules
  content {
    from_port   = rule.value.from
    to_port     = rule.value.to
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}
medium
A. for_each must be a map, not a list
B. Missing quotes around "ingress" in dynamic block
C. Incorrect reference to loop variable; should use ingress.value instead of rule.value
D. cidr_blocks cannot be a list

Solution

  1. Step 1: Check loop variable usage inside dynamic block

    Inside a dynamic block named "ingress", the loop variable is ingress, not rule.
  2. Step 2: Correct variable reference

    References should be ingress.value.from and ingress.value.to to access current item values.
  3. Final Answer:

    Incorrect reference to loop variable; should use ingress.value instead of rule.value -> Option C
  4. Quick Check:

    Loop variable matches dynamic block name [OK]
Hint: Loop variable = dynamic block name [OK]
Common Mistakes:
  • Using wrong loop variable name
  • Omitting quotes around dynamic block name
  • Misunderstanding for_each data types
5. You want to create a security group with dynamic ingress rules from a list of maps, but only include rules where enabled is true. Which Terraform code snippet correctly uses a dynamic block with filtering?
variable "rules" {
  default = [
    { from = 22, to = 22, enabled = true },
    { from = 80, to = 80, enabled = false },
    { from = 443, to = 443, enabled = true }
  ]
}

resource "aws_security_group" "filtered" {
  name = "filtered-sg"

  dynamic "ingress" {
    for_each = ???
    content {
      from_port   = ingress.value.from
      to_port     = ingress.value.to
      protocol    = "tcp"
      cidr_blocks = ["0.0.0.0/0"]
    }
  }
}
hard
A. for_each = [for r in var.rules : r if r.enabled]
B. for_each = var.rules
C. for_each = { for r in var.rules : r.from => r if r.enabled }
D. for_each = filter(var.rules, r => r.enabled)

Solution

  1. Step 1: Understand filtering with for expressions

    Terraform uses for expressions with an if condition to filter lists, e.g. [for r in var.rules : r if r.enabled].
  2. Step 2: Apply filtered list to for_each

    Using this filtered list in for_each creates dynamic blocks only for enabled rules.
  3. Final Answer:

    for_each = [for r in var.rules : r if r.enabled] -> Option A
  4. Quick Check:

    Use for expression with if to filter list [OK]
Hint: Filter list with for expression and if condition [OK]
Common Mistakes:
  • Using unfiltered list causing unwanted rules
  • Trying to use non-existent filter() function
  • Using map instead of list for for_each
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