Friday, 29 November 2024

Use Dynamic Block Conditionally in Terraform with a Nested Block

 We have discussed  usage of dynamic blocks conditionally in terraform in the post "Use Dynamic Block Conditionally in Terraform". Sometimes we need to use dynamic with nested blocks. In this post look at such an example of using dynamic block with network rules for Azure event hubs.

We are going to setup a nested dynamic block as shown below.


To understand it fully let's inspect what is the change done above. Before the change the code was simple as shown below. It was allowing two virtual networks to each event hub.

resource "azurerm_eventhub_namespace" "instanceeventhub" {
  count                    = 4
  name                     = "myeventhub-${count.index + 1}"
  location                 = var.location
  resource_group_name      = var.rg_name
  sku                      = "Standard"
  capacity                 = 1
  auto_inflate_enabled     = true
  maximum_throughput_units = 20

  network_rulesets {
    default_action                 = "Deny"
    trusted_service_access_enabled = true
    ip_rule = [
      {
        ip_mask = "99.0.999.999/32"
        action  = "Allow"
      },
      {
        ip_mask = "222.222.222.222/32"
        action  = "Allow"
      }
    ]

    virtual_network_rule {
      subnet_id = var.aks_subnet_id
    }
    virtual_network_rule {
      subnet_id = var.subnet_id
    }
  }
}

Now our requirement is to allow additional subnet only in development environemnt. To ahcive that we should use dynamic block for virtual_network_rule . However, since it is nested inside the network_rulesets block  we cannot use the simple approach as described in the "Use Dynamic Block Conditionally in Terraform".


That is if we try to use 

dynamic "virtual_network_rule" {
 for_each = var.environment_name == "Dev" ? local.eh_network_ruleset_temp : local.eh_network_ruleset        
content {
   subnet_id = virtual_network_rule.value.subnet_id
  }
}

there will be errors saying does not have a variable var  or local.


 As the variables or local variables are not accessible within the nested block. So above dynamic block does not work.


Instead we will have to declare a  map variable with a bit of complexity as shown below. Here when we use the eh_network_ruleset_temp, it is added with three eh_network_rules, with three subnet ids. The eh_network_ruleset is only added with two eh_network_rules.

locals {
 
  eh_network_ruleset_temp = {
    eh_network_rules = [
      {
        subnet_id = var.aks_subnet_id
      },
      {
        subnet_id = var.subnet_id
      },
      {
        subnet_id = var.build_agent_subnet_id # only for dev env
      }
    ]
  }

  eh_network_ruleset = {
    eh_network_rules = [
      {
        subnet_id = var.aks_subnet_id
      },
      {
        subnet_id = var.subnet_id
      }
    ]
  }
}

variable "environment_name" {
  description = "Name of environment"
}

Then we can the rules as shown below. So the outer block refers to the variables.

dynamic "network_rulesets" {
    for_each = var.environment_name == "Dev" ? local.eh_network_ruleset_temp : local.eh_network_ruleset
    content {
      default_action                 = "Deny"
      trusted_service_access_enabled = true
      ip_rule = [
        {
        ip_mask = "99.0.999.999/32"
        action  = "Allow"
      },
      {
        ip_mask = "222.222.222.222/32" 
        action  = "Allow"
      }
      ]

      dynamic "virtual_network_rule" {
        for_each = network_rulesets.value
        content {
          subnet_id = virtual_network_rule.value.subnet_id
        }
      }
    }
  }

Then the inner virtual_network_rule block can use dynamically the value from outer block. see usage of for_each = network_rulesets.value .

       
dynamic "virtual_network_rule" {
        for_each = network_rulesets.value
        content {
          subnet_id = virtual_network_rule.value.subnet_id
        }
      }

The full event hub resource code is as below using nested dynamic blocks.

resource "azurerm_eventhub_namespace" "instanceeventhub" {
  count                    = 4
  name                     = "myeventhub-${count.index + 1}"
  location                 = var.location
  resource_group_name      = var.rg_name
  sku                      = "Standard"
  capacity                 = 1
  auto_inflate_enabled     = true
  maximum_throughput_units = 20

  dynamic "network_rulesets" {
    for_each = var.environment_name == "Dev" ? local.eh_network_ruleset_temp : local.eh_network_ruleset
    content {
      default_action                 = "Deny"
      trusted_service_access_enabled = true
      ip_rule = [
        {
        ip_mask = "99.0.999.999/32"
        action  = "Allow"
      },
      {
        ip_mask = "222.222.222.222/32" 
        action  = "Allow"
      }
      ]

      dynamic "virtual_network_rule" {
        for_each = network_rulesets.value
        content {
          subnet_id = virtual_network_rule.value.subnet_id
        }
      }
    }
  }
}


No comments:

Popular Posts