Terraform で VPC + RDS + Lambda あたりの構成を1から書いている。AIに指導役になってもらって、自分でコードを書いては都度レビューしてもらうという進め方。
Security Group まわりで何回も指摘をもらったので、備忘録として残しておく。
型がバラバラで混乱する
Security Group の各フィールドを並べると型がバラバラで最初は戸惑った。
from_port = 0 → number
protocol = "-1" → string
cidr_blocks = ["0.0.0.0/0"] → list(string)
なぜ -1 は数値じゃなくて文字列なのか、という疑問が湧いたけど、これは Terraform が勝手に決めているわけではなく AWS の API 仕様がそうなっている。型に迷ったら terraform validate を実行すればエラーで教えてくれる。
ingress に = を付けてしまう
# NG
ingress = {
from_port = 5432
}
# OK
ingress {
from_port = 5432
to_port = 5432
protocol = "tcp"
security_groups = [aws_security_group.lambda.id]
}
tagsの書き方で勢いのまま、同じ書き方をしてしまった。
指摘を受けて = を外したら通ったけど、なぜ tags には = が付いて ingress には付かないのかが分からなかった。どっちも { } で囲んでいるのに何が違うのか。
そもそも map 型とは
tags の書き方を改めて見るとこうなっている。
tags = {
Name = "my-rds-sg"
Environment = "dev"
}
これは map 型(キーと値のペアの集まり)の値を tags という属性に代入している。Python でいう辞書、JSON でいうオブジェクトに近い。キーも値も自由に決められて、Project = "tiled" とか好きなものを足せる。
ポイントは tags は「1つの値」を受け取っているということ。その値がたまたま { } を使う map 型なだけで、やっていることは vpc_id = "vpc-xxxxx" と同じ「属性への代入」。
ブロックとの違い
一方 ingress はブロック。
ingress {
from_port = 5432
to_port = 5432
protocol = "tcp"
security_groups = [aws_security_group.lambda.id]
}
ブロックは「この種類の設定を1セット定義する」という構造で、値の代入ではない。中に書けるフィールドは Provider 側で決まっていて、自由にキーを足すことはできない。そして複数書けばルールを複数作れる。
# ingressブロックを2つ書けば、ルールが2つできる
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 5432
to_port = 5432
protocol = "tcp"
security_groups = [aws_security_group.lambda.id]
}
見分け方
= が付く → 属性(値を代入している。map型でも代入は代入)
= が付かない → ブロック(設定の構造を定義している)
見た目が似ているので最初は混乱したけど、代入か、定義かで考えるとすっきりした。
security_groups をリストにしなかった
# NG
security_groups = aws_security_group.lambda.id
# OK
security_groups = [aws_security_group.lambda.id]
security_groups は list(string) 型。1つしか指定しなくても [] で囲む必要がある。cidr_blocks と同じ。
RDS SG の最終形
上の間違いを全部直すとこうなった。
resource "aws_security_group" "rds" {
name = "${var.project_name}-rds-sg-${var.environment}"
description = "RDS PostgreSQL access from Lambda only"
vpc_id = aws_vpc.main.id
ingress {
from_port = 5432
to_port = 5432
protocol = "tcp"
security_groups = [aws_security_group.lambda.id]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "${var.project_name}-rds-sg-${var.environment}"
}
}
Terraform は型付き言語なので、フィールドの型を間違えると容赦なく怒られる。迷ったら terraform validate で確認するのが一番早い。