Terraformでdynamicブロックを使う

AzureFirewallを構築した際に、
｢これアプリケーションルールだけループして処理したいな…dynamicブロック使ってみるか！｣となりdynamicブロックを使ってみました。
その備忘録。

# dynamicブロックとは
AzureFirewallのアプリケーションルールの基本構造は、以下のようになります。

```terraform
resource "azurerm_firewall_application_rule_collection" "example" {
  name                = "testcollection"
  azure_firewall_name = azurerm_firewall.example.name
  resource_group_name = azurerm_resource_group.example.name
  priority            = 100
  action              = "Allow"

  rule {
    name = "testrule"

    source_addresses = [
      "10.0.0.0/16",
    ]

    target_fqdns = [
      "*.google.com",
    ]

    protocol {
      port = "443"
      type = "Https"
    }
  }
}
```

見ての通り、`resourseブロック`の中に`ruleブロック`がありますね。さらに`ruleブロック`の中に`protocalブロック`があります。
このような構造には、「ここの部分だけループ処理できるよ」という意味合いが込められています。
これをループ処理するのに利用するのが、`dynamicブロック`なわけです。

# dynamicブロックの基本構造
dynamicブロックは、以下のように記載します。

```terraform
dynamic "ブロック名"{
    for_each = {
    # mapでまとめたループ処理で処理させたい情報
    }
    content{
    # 繰り返したい内容を書く
    }
}

```
ブロック名はループさせたいブロック名を入れます。今回はruleですね。
`for_each`を利用するので、ループ内で処理させたい情報は`map`にまとめておく必要があります。

# 実際に利用してみる

以上より、AzureFirewallのアプリケーションルールを`Dynamicブロック`で処理すると…

```terraform
# 最初にmapを定義する
locals {
  firewall_application_rule = [{
    rule_name        = "testrule1",
    source_addresses = "10.0.0.0/16"
    target_fqdns     = "*.google.com"
    port             = "443"
    type             = "Https"
    }, {
    rule_name        = "testrule2",
    source_addresses = "10.1.0.0/16"
    target_fqdns     = "*.google.com"
    port             = "443"
    type             = "Https"

  }]
}

resource "azurerm_firewall_application_rule_collection" "example" {
  name                = "testcollection"
  azure_firewall_name = azurerm_firewall.example.name
  resource_group_name = azurerm_resource_group.example.name
  priority            = 100
  action              = "Allow"

  dynamic "rule" {
    for_each = { for i in local.firewall_application_rule : i.rule_name => i }
    # 以下はループで処理される
    content {
      name             = rule.value.rule_name
      source_addresses = [rule.value.source_addresses]
      target_fqdns     = [rule.value.target_fqdns]
      protocol {
        port = rule.value.port
        type = rule.value.type
      }
    }
  }
}
```
このようになります。
※もちろんリソースグループやサブネット、AzureFirewallなどのresourceも必要です。今回は省略しています

`tuple`を利用した`for_each`の使い方については別途記事を書いているので、そちらをご確認ください。

https://qiita.com/hikaru_motomiya/items/fdd784adb5134c31120c

注意すべきは、単なる`for_each`で利用する場合は`each.value.rule_name`とするべきところが、`dynamicブロック`を利用する際は`[ブロック名].value.rule_name`とする必要があることですね。

出力
```terminal
(抜粋)
# azurerm_firewall_application_rule_collection.example will be created
  + resource "azurerm_firewall_application_rule_collection" "example" {
      + action              = "Allow"
      + azure_firewall_name = "firewall"
      + id                  = (known after apply)
      + name                = "testcollection"
      + priority            = 100
      + resource_group_name = "rg-test"

      + rule {
          + name             = "testrule1"
          + source_addresses = [
              + "10.0.0.0/16",
            ]
          + target_fqdns     = [
              + "*.google.com",
            ]

          + protocol {
              + port = 443
              + type = "Https"
            }
        }
      + rule {
          + name             = "testrule2"
          + source_addresses = [
              + "10.1.0.0/16",
            ]
          + target_fqdns     = [
              + "*.google.com",
            ]

          + protocol {
              + port = 443
              + type = "Https"
            }
        }
    }
```
このようにちゃんと2つのルールが作られていますね。
dynamicブロックの中でdynamicブロックを利用するなんてことも可能ですが、dynamicブロックは使いすぎるとコードがわけわからなくなっちゃうので注意が必要です。
dynamicブロックのご利用はご計画的に。


# 参考
https://developer.hashicorp.com/terraform/language/expressions/dynamic-blocks

https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/firewall_application_rule_collection