Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Terrafrom dynamic block with dynamic content

I am trying to create a terraform module for aws_route_table creation, here is an example of this resource definition:

resource "aws_route_table" "example" {
  vpc_id = aws_vpc.example.id

  route {
    cidr_block = "10.0.1.0/24"
    gateway_id = aws_internet_gateway.example.id
  }

  route {
    ipv6_cidr_block        = "::/0"
    egress_only_gateway_id = aws_egress_only_internet_gateway.example.id
  }

  tags = {
    Name = "example"
  }
}

I am trying to make it more dynamic by using dynamic blocks. The problem is that I always have to define the keys in content block

resource "aws_route_table" "example" {
...

   dynamic "route" {
        for_each = var.route
        content {
            cidr_block  = route.value.cidr_block  
            gateway_id  = route.value.gateway_id  
        }
    }

...
}

So in this case, I will need to write two dynamic blocks, one for the content with cidr_block and gateway_id and one for the content with ipv6_cidr_block and egress_only_gateway_id.

Is there any way to do this without defining keys explicitly. Something like this:

   dynamic "route" {
        for_each = var.route
        content {
          var.route.map
        }
    }
like image 358
Kingindanord Avatar asked May 05 '26 08:05

Kingindanord


2 Answers

Yes, you can create route dynamically, because block route acts as Attributes as Blocks. So you can do (example)

# define all your routes in a variable (example content)

variable "routes" {

  default = [
    {
      cidr_block = "0.0.0.0/0"
      gateway_id = "igw-0377483faa64bf010"
    },
    {
      cidr_block = "172.31.0.0/20"
      instance_id = "i-043fc97db72ad1b59"     
    }
  ]
}


# need to provide default values (null) for all possibilities
# in route

locals {

  routes_helper = [
      for route in var.routes: merge({
          carrier_gateway_id = null
          destination_prefix_list_id = null
          egress_only_gateway_id = null 
          ipv6_cidr_block = null 
          local_gateway_id = null
          nat_gateway_id = null
          network_interface_id = null
          transit_gateway_id = null 
          vpc_endpoint_id = null 
          instance_id = null
          gateway_id = null
          vpc_peering_connection_id = null
      }, route)
    ]

}


resource "aws_route_table" "example" {
  vpc_id = aws_vpc.example.id

  # route can be attribute, instead of blocks
  route = local.routes_helper

  tags = {
    Name = "example"
  }
}

Docs do not recommend to use that in general, but I think route is a good example where this would be acceptable.

like image 52
Marcin Avatar answered May 07 '26 22:05

Marcin


The general answer for a block type that doesn't employ the "Attributes as Blocks" backward-compatibility shim Marcin mentioned is to rely on the fact that for Terraform provider arguments there is no difference in meaning between omitting an argument or setting it explicitly to null.

That means that you can write an expression to decide whether to populate a particular argument or not, by making it return an explicit null in the appropriate cases. For example:

  dynamic "route" {
    for_each = var.route
    content {
      cidr_block      = try(route.value.cidr_block, null)
      gateway_id      = try(route.value.gateway_id, null)
      ipv6_cidr_block = try(route.value.ipv6_cidr_block, null)
      # ...
    }
  }

A nested block like this is more like a fixed data structure than a collection, so there isn't any syntax to construct it dynamically.

With that said, in this particular case route is, as Marcin noted, actually really just an attribute of an object type, and Terraform Core is allowing to use it with block syntax as a concession to backward compatibility with older Terraform versions. For that reason, there's not really any significant difference between these two approaches in practice, but the "Attributes as Blocks" mechanism is only for certain situations where providers were design to assume Terraform v0.11 validation bugs and so the conditional null approach I described above is the more general answer, which should work for normal nested blocks too.

like image 26
Martin Atkins Avatar answered May 07 '26 22:05

Martin Atkins



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!