多重階層となっているmap変数をループで処理する(Terraform)

この記事は約8分で読めます。

TerraformでGCPのファイアウォール ルールを複数作成するあたり、似たり寄ったりの項目を1つずつコーディングするのはナンセンスかなと思い、map変数に定義し、ループ処理で動的にファイアウォール ルールを複数作成するコードを書いてみました。

ただ、

  • プロトコル
  • ポート

については、map変数内に更にmap変数で定義(map変数の多重化)することになるので、ループ処理も多重ループ化する必要があり、この辺少し苦戦しました。

スポンサーリンク

サンプルコード

# lbへアクセス可能なIPレンジを取得
data "google_compute_lb_ip_ranges" "lb_allow_ip_ranges" {
}

# リソース構築リージョンのsubnetを取得
data "google_compute_subnetwork" "subnetwork" {
  name       = local.vpc_name
  region     = local.region
  depends_on = [google_compute_network.vpc_network]
}

locals {
  firewall-rule = {
    # Firewall Rule(LB)
    firewall-allow-lb = {
      name     = "allow-http-from-lb"
      priority = 1000

      # Allow IP
      direction     = "INGRESS"
      source_ranges = "${data.google_compute_lb_ip_ranges.lb_allow_ip_ranges.network}"
      allow_ports = [
        {
          protocol = "tcp"
          ports    = ["80", "443"]
        }
      ]
    }

    # Firewall Rule(SSH Maintenance)
    firewall-allow-ssh = {
      name     = "allow-ssh"
      priority = 1000

      # Allow IP
      direction     = "INGRESS"
      source_ranges = "${[local.ip_white_list]}"
      allow_ports = [
        {
          protocol = "tcp"
          ports    = ["22"]
        }
      ]
    }

    # Firewall Rule(Internal)
    firewall-allow-Internal = {
      name     = "allow-internal"
      priority = 65534

      # Allow IP
      direction     = "INGRESS"
      source_ranges = "${[data.google_compute_subnetwork.subnetwork.ip_cidr_range]}"
      allow_ports = [
        {
          protocol = "tcp"
          ports    = ["80", "443", "5432"]
        },
        {
          protocol = "udp"
          ports    = ["80", "443", "5432"]
        },
        {
          protocol = "icmp"
          ports    = []
        }
      ]
    }

  }

  # VPC
  vpc_name = "test-vpc"

  # tag
  target_tags = ["hoge-server"]
}

# VPC
resource "google_compute_network" "vpc_network" {
  name = local.vpc_name
}

# 内部IP設定
resource "google_compute_global_address" "private_ip_address" {
  name          = "tutorial-private-ip-address"
  purpose       = "VPC_PEERING"
  address_type  = "INTERNAL"
  prefix_length = 20
  network       = google_compute_network.vpc_network.id
}

# VPCネットワークピアリング作成
resource "google_service_networking_connection" "private_vpc_connection" {
  network                 = google_compute_network.vpc_network.id
  service                 = "servicenetworking.googleapis.com"
  reserved_peering_ranges = [google_compute_global_address.private_ip_address.name]
}

# Firewall Rule
resource "google_compute_firewall" "firewall-rule" {
  count         = length(local.firewall-rule)
  name          = values(local.firewall-rule)[count.index].name
  network       = google_compute_network.vpc_network.name
  priority      = values(local.firewall-rule)[count.index].priority
  target_tags   = local.target_tags
  direction     = values(local.firewall-rule)[count.index].direction
  source_ranges = values(local.firewall-rule)[count.index].source_ranges

  # 許可プロトコル/ポートの設定
  dynamic "allow" {
    for_each = values(local.firewall-rule)[count.index].allow_ports
    content {
      protocol = allow.value.protocol
      ports    = allow.value.ports
    }
  }
}

スポンサーリンク

コードの解説

処理の概要

VPCを作成し、そのVPCに対し、

  • ingress:TCP 80,443ポートを許可
  • ingress:特定の送信元IPアドレスであればTCP 22ポートを許可
  • VPC内部用

以上3つのファイアフォール ルールをループ処理で動的に作成するものとなっています。

map変数をvariables化したり、.tfvarsファイルに記述したりなど、コード設計によりやり方は様々あると思いますが、terraform -varで変数の値の書き換えが可能なため、書き換えを許可したくない場合は、localsにてローカル変数化した方がよいと個人的に思っています。

map変数の階層ですが、2階層までにとどめておいた方が、可読性を担保出来ると思いますし、多重ループ処理もループ処理のコーディング規約で推奨されている2重ループまでというのを遵守出来ます。

これ以上階層が深くなる場合は変数の持たせ方やコード設計を見直した方がよいと思います。

多重ループ

ファイアウォール ルールの固定長項目の代入はcountにて行い、可変長項目の許可(または拒否)プロトコル、ポートについては、

  • dynamic
  • for_each

にて動的に代入、項目作成を行っています。

多重ループを行う際、for_eachを2回書くのでは?と最初は思ったのですが、Terraformのコードチェックでエラーとなるので、無理なようです。

なので、countとfor_eachの組み合わせで2重ループを行っています。

条件判定やvariables変数に代入する際は、forを利用します。

他補足

1 – 3行目の

# lbへアクセス可能なIPレンジを取得
data "google_compute_lb_ip_ranges" "lb_allow_ip_ranges" {
}

ですが、こちらロードバランサのingress allow ip rangeを取得するものとなっています。

https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/compute_lb_ip_ranges

こちらを利用することで、21行目のsource_rangesにip range(cidr形式)をハードコーディングしなくて済むようになります。

Consoleに表示されているロードバランサのingress allow ip rangeですが、変更される可能性があり、ハードコーディングしているとある日突然、通信が出来なくなるといった事が起きる可能性は十分にあります。

GCP側で変更される可能性がある値については、dataブロックでGCPの値を参照・取得し、利用するようにした方がベターです。

スポンサーリンク

まとめ

Terraformで多重ループ化するにあたり、やり方が分からず結構苦戦しました。

この記事がお役に立てば幸いです。

スポンサーリンク
GCPTerraform
ヤマログ
タイトルとURLをコピーしました