この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
ちゃだいん(@chazuke4649)です。
普段、IaCでAWSリソースを管理している人は、セキュリティグループについて効率よく書けないか、一度は頭をひねった経験があるのではないでしょうか?
自分もその一人でして、今回Terraformでセキュリティグループを定義するにあたり、いろんな方の記事をありがたく参考にしつつ、改善することができたので紹介します。
改善前
改善前は以下のようにセキュリティグループを定義していました。
resource "aws_security_group" "test" {
vpc_id = aws_vpc.main.id
name = "test-sg"
description = "For test"
tags = { Name = "test-sg" }
}
## Inbound
resource "aws_security_group_rule" "test_inbound_01" {
security_group_id = aws_security_group.test.id
type = "ingress"
description = "HTTP from Internet"
protocol = "tcp"
from_port = "80"
to_port = "80"
cidr_blocks = ["0.0.0.0/0"]
}
resource "aws_security_group_rule" "test_inbound_02" {
security_group_id = aws_security_group.test.id
type = "ingress"
description = "SSH From Bastion"
protocol = "tcp"
from_port = "22"
to_port = "22"
source_security_group_id = ["sg-1234567890"]
}
## Outbound
resource "aws_security_group_rule" "test_outbound_01" {
security_group_id = aws_security_group.test.id
type = "egress"
description = "Allow any outbound traffic"
protocol = "-1"
from_port = "0"
to_port = "0"
cidr_blocks = ["0.0.0.0/0"]
}
これを修正し、結果的に以下のようになりました。
改善後
locals {
test_sg = {
## [ type, from_port, to_port, protocol, sg-id, cidr_blocks, description ]
"rule_1" = ["ingress", 80, 80, "tcp", null, ["0.0.0.0/0"], "HTTP from Internet"],
"rule_2" = ["ingress", 22, 22, "tcp", "sg-1234567890", null, "SSH from Bastion"],
"rule_3" = ["egress", 0, 0, "-1", null, ["0.0.0.0/0"], "Allow any outbound traffic"]
}
}
resource "aws_security_group" "test_sg" {
vpc_id = aws_vpc.main.id
description = "For test"
name = "test-sg"
tags = { Name = "test-sg" }
}
resource "aws_security_group_rule" "test" {
security_group_id = aws_security_group.test_sg.id
for_each = local.test_sg
type = each.value[0]
from_port = each.value[1]
to_port = each.value[2]
protocol = each.value[3]
source_security_group_id = each.value[4]
cidr_blocks = each.value[5]
description = each.value[6]
}
解説
改善するにあたり、冗長的な記述になっているセキュリティグループルールをコンパクトにできないか考えます。
for_each
を使うと、例えばresourceブロックを繰り返し処理で複数作ることができます。こちらは比較的人気かと思いますので説明は割愛します。詳しくは参考ブログをご覧ください。
for_each
によって複数作れそうですが、ルールの内容によって一部引数に差異があります。ここをどう解決するかが肝になりそうです。条件分岐を入れると複雑化しそうなので、できるだけシンプルな方法を検討しました。
今回のポイントは null 型です。
null: 不在または省略を表す値。リソースの引数をnullにすると、Terraformは完全に省略したかのように振る舞います - 引数にデフォルト値がある場合はそれを使用し、引数が必須の場合はエラーを発生させます。nullは条件式で最も有用なので、条件が満たされない場合に動的に引数を省略することができます。(意訳)
Types and Values - Configuration Language | Terraform by HashiCorp
今回の場合、aws_security_group_rule
ブロックの引数source_sercurity_group_id
とcidr_blocks
は、両方入力することができず、いずれか一方のみしか入力できません。
例えばインバウンドの80番ポートでcidr_blocks
を0.0.0.0/0
で開放する場合、source_security_group_id
に""
とブランクにしてplanするとエラーになります。
│ Error: Conflicting configuration arguments
│
│ with aws_security_group_rule.test["rule_1"],
│ on security_group.tf line 217, in resource "aws_security_group_rule" "test":
│ 217: source_security_group_id = each.value[4]
│
│ "source_security_group_id": conflicts with cidr_blocks
そこでブランクの代わりにnull
を入力すると、対象の引数は省略されたものとみなし、競合しなくなるというわけです。
null
の活用により、単一のセキュリティグループルールをガワだけ作り、実際の値はローカル変数にてMAP型の行を追加するだけで、ルールを追加することができます。
これによって、修正前よりコード量を減らし同等の構成を再現することができました。
紹介としては以上となります。もっといい方法があったらぜひTwitterなどでこそっと教えてください。
参考URL
aws_security_group | Resources | hashicorp/aws | Terraform Registry
aws_security_group_rule | Resources | hashicorp/aws | Terraform Registry
以下の記事を参考にしました。ありがとうございます!
Terraformerとしてコードを書いて思うこと | フューチャー技術ブログ
TerraformでAWSのセキュリティグループをListで定義してみた - Kumanote corporate website