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を取得するものとなっています。
こちらを利用することで、21行目のsource_ranges
にip range(cidr形式)をハードコーディングしなくて済むようになります。
Consoleに表示されているロードバランサのingress allow ip rangeですが、変更される可能性があり、ハードコーディングしているとある日突然、通信が出来なくなるといった事が起きる可能性は十分にあります。
GCP側で変更される可能性がある値については、dataブロックでGCPの値を参照・取得し、利用するようにした方がベターです。
まとめ
Terraformで多重ループ化するにあたり、やり方が分からず結構苦戦しました。
この記事がお役に立てば幸いです。