A Dynamic block is similar to for expression but creates 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. Unlike the count block, which will iterate over the resource, the dynamic will reside inside the resource block.
Dynamic blocks are supported inside the following blocks,
- Resource
- Data
- Provider
- Provisioner
Dynamic blocks can be used for resources like Setting, Security Group, etc.
Let us consider the example of creating a security group with multiple ingress rules for ports (80, 443, 3306, and 22). In an ideal scenario, we will end up copy-pasting the ingress rule for each port in the security group resource block.
Security Group (Without Dynamic Block)
// Security Group without Dynamic Block resource "aws_security_group" "lamp_securitygroup_basic" { name = "LAMP Security Group Basic" description = "LAMP Server Security Group Basic ${terraform.workspace}" // call the workspace vpc_id = data.aws_vpc.get_vpc_id.id tags = local.common_tags // in-bound rules ingress { from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] ipv6_cidr_blocks = ["::/0"] } ingress { from_port = 443 to_port = 443 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] ipv6_cidr_blocks = ["::/0"] } ingress { from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] ipv6_cidr_blocks = ["::/0"] } ingress { from_port = 3306 to_port = 3306 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] ipv6_cidr_blocks = ["::/0"] } // outbound rules egress { from_port = 0 to_port = 0 protocol = -1 cidr_blocks = ["0.0.0.0/0"] ipv6_cidr_blocks = ["::/0"] } }
The above code will work perfectly fine, but as your security group rules go on increasing, you will need to copy-paste the ingress block for each rule, which will make the code hard to maintain and update.
To overcome this issue, Terraform provides us with a dynamic block. We can write the same code using dynamic block,
Security Group (Dynamic Block)
// Data block for VPC ID data "aws_vpc" "get_vpc_id" { default = true }
// variable declaration variable "port" { description = "Enter the ports to be configured in SG Ingress rule" default = [80, 22, 443, 3306] }
// Create Security Group uisng Dynamic Block resource "aws_security_group" "lamp_sg" { name = "LAMP Security Group" description = "LAMP Server Security Group ${terraform.workspace}" // call the workspace vpc_id = data.aws_vpc.get_vpc_id.id tags = local.common_tags dynamic "ingress" { for_each = var.port content { description = "Security rule for inbound ${ingress.value}" from_port = ingress.value // The block should use block name to fetch the value and key to_port = ingress.value protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] ipv6_cidr_blocks = ["::/0"] } } // outbound rules egress { from_port = 0 to_port = 0 protocol = -1 cidr_blocks = ["0.0.0.0/0"] ipv6_cidr_blocks = ["::/0"] } }
In our example, we are producing a nested block of ingress rules,
- The label of our dynamic block is “ingress”, you can change the label, as you per the requirement, like if you want to generate “setting” nested blocks we can name the dynamic block as “setting”.
- For_each – Argument will provide the complex argument to iterate over.
- Content Block – defines the body of each generated block. In our example it will be the ingress block, iterating over multiple ports.
In multi-level nested block, we can have multi-level nested dynamic blocks, ie the nested dynamic bock within the dynamic block.