0
0
Terraformcloud~7 mins

Dynamic blocks in security groups in Terraform - Commands & Configuration

Choose your learning style9 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.