見出し画像

【混ぜるな危険】Terraformのaws_security_groupとaws_security_group_ruleの競合の話

2者の用法についてメモ

混ぜるな危険とは

この両者を下記の条件で使用した場合に発生する競合による上書きと伴うapply時の毎回changedの状態を指します。

・同一セキュリティグループで使用し、
・aws_security_groupにてインラインルール定義を行った場合

下記aws_security_groupモジュールの説明ページの1つ目の注意事項に書いてあるとおりですが。

セキュリティグループとセキュリティグループルールに関する注意事項

Terraformは現在、スタンドアロンのSecurity Group Ruleリソース(単一のingressまたはegressルール)と、ingressおよびegressルールがインラインで定義されたSecurity Groupリソースの両方を提供しています。
現時点では、インライン・ルールを持つセキュリティ・グループを、他のセキュリティ・グループ・ルール・リソースと組み合わせて使用することはできません。ルール設定の競合が発生し、ルールが上書きされてしまいます。

具体的にどうしたらどうなるのか

下記のように80/tcpへのaccessをsecurity groupを定義し

# webサーバのSG
resource aws_security_group web {
 vpc_id = var.vpc_id
 name   = "ama2-web-sg"
 ingress {
   from_port = 80
   to_port   = 80
   protocol  = "tcp"
   cidr_blocks = ["x.x.x.x/32"]
 }
 egress {
   from_port   = 0
   to_port     = 0
   protocol    = "-1"
   cidr_blocks = ["0.0.0.0/0"]
 }
 description = "for web servers"
 tags = {
   Name        = "ama2-web-sg"
   environment = var.environment
 }
}


さらにaws_security_group_ruleで追加した場合、

# webサーバのSG
resource aws_security_group web {
 vpc_id = var.vpc_id
 name   = "ama2-web-sg"
 ingress {
   from_port = 80
   to_port   = 80
   protocol  = "tcp"
   cidr_blocks = ["x.x.x.x/32"]
 }
 egress {
   from_port   = 0
   to_port     = 0
   protocol    = "-1"
   cidr_blocks = ["0.0.0.0/0"]
 }
 description = "for web servers"
 tags = {
   Name        = "ama2-web-sg"
   environment = var.environment
 }
}

resource aws_security_group_rule web_https {
 security_group_id = aws_security_group.web.id
 from_port         = 443
 to_port           = 443
 protocol          = "tcp"
 type              = "ingress"
 cidr_blocks       = ["x.x.x.x/32"]
 description       = "https access"
}

毎回changedが発生します。

しかも、443/tcpのルールが削除されたり復活したりの状態を繰り返すことになります。

解決方法

aws_security_group_ruleを使用する場合は、aws_security_groupのインラインルールを使用しないようにします。

前述の例は下記のように書き換えます

# webサーバのSG
resource aws_security_group web {
 vpc_id = var.vpc_id
 name   = "ama2-web-sg"
 egress {
   from_port   = 0
   to_port     = 0
   protocol    = "-1"
   cidr_blocks = ["0.0.0.0/0"]
 }
 description = "for web servers"
 tags = {
   Name        = "ama2-web-sg"
   environment = var.environment
 }
}

resource aws_security_group_rule web_https {
 security_group_id = aws_security_group.web.id
 from_port         = 443
 to_port           = 443
 protocol          = "tcp"
 type              = "ingress"
 cidr_blocks       = ["x.x.x.x/32"]
 description       = "https access"
}
resource aws_security_group_rule web_http {
 security_group_id = aws_security_group.web.id
 from_port         = 80
 to_port           = 80
 protocol          = "tcp"
 type              = "ingress"
 cidr_blocks       = ["x.x.x.x/32"]
 description       = "https access"
}


ユースケース

大抵の場合はaws_security_groupにインラインルールを記載することで対応可能です。

ではどんな場合にaws_security_group_ruleが必要になるかというと、1つはsecurity group idを相互に参照する設定が必要な場合です。

例えばALB - Target Groupのインスタンス相互のsecurity group設定がそれにあたります。

ALBのsecurity group設定について、AWSの推奨は下記のとおりです。
・ALBのegressはTarget Groupのインスタンスのsecurity groupを指定する
・Target Groupのインスタンスのルールでは ingressでALBのsecurity group idを指定する

これをaws_security_groupのみで実装することはできません。循環参照が発生するからです。

そのため、下記のようにaws_security_group_ruleにて追加ルールとして設定する必要があります。

resource aws_security_group_rule alb_outbound {
 security_group_id        = aws_security_group.alb.id
 from_port                = 80
 to_port                  = 80
 protocol                 = "tcp"
 type                     = "egress"
 source_security_group_id = aws_security_group.app.id
 description              = "outbound to target group instances"
}

Terraformでの作成順がsecurity group -> security group ruleであるためこの設定により循環参照が回避され、設定が可能になります。

参考

こちらのブログでも紹介されていました。
参考にさせていただきました、ありがとうございます。



この記事が気に入ったらサポートをしてみませんか?