Terraformでaws_security_groupとaws_security_group_ruleの両方でルールを設定すると競合する
前回はセキュリティグループIDをソースに指定してセキュリティグループを作成する方法について書きましたが、今回はその流れでルールを書いていたらハマった話です。
セキュリティグループIDをソースに指定してセキュリティグループを作成するユースケースとして、AWSで踏み台サーバを用意してアプリケーションサーバにはその踏み台サーバ経由でしかsshできないようにする場合を想定していました。
例えば、そのうえで「アプリケーションサーバには特定のIPからの80番ポートでのHTTPを許可する」というルールを追加するということも考えられます。
CIDRブロックでのingressルールはaws_security_groupにそのまま記述できるので、例えば以下のように書けます。
provider "aws" { region = "ap-northeast-1" } resource "aws_security_group" "bastion" { (略) } resource "aws_security_group" "application" { name = "application" description = "Security Group for application" // ここを新しく追加 ingress { from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } } resource "aws_security_group_rule" "application_from_bastion" { type = "ingress" to_port = 22 protocol = "tcp" source_security_group_id = aws_security_group.bastion.id from_port = 22 security_group_id = aws_security_group.application.id }
これは問題なく実行でき、意図通りの設定になります。
問題はこれからで、上記のtfファイルを特に設定を書き換えることなく、 terraform plan
を実行すると、変更があると言われてしまいます。
Terraform will perform the following actions: # aws_security_group.application will be updated in-place ~ resource "aws_security_group" "application" { (略) } Plan: 0 to add, 1 to change, 0 to destroy.
そんなまさかと思いつつ、 terraform apply
を実行すると、確かに変更が適用され、コンソールで確認するとaws_security_group_ruleで指定したSSHのルールが削除されています。
さらに、この状態で再度 terraform plan
terraform apply
を実行すると、もとに戻り、無限ループ(ずっと terraform plan
で差分が出る)になります。
これを回避するためには、全てのルールをaws_security_group内かaws_security_group_ruleかどちらかに統一すれば良いです。
今回はセキュリティグループIDをソースに指定する指定方法をそのまま生かしたいので、aws_security_group_ruleに統一します。
ingressのルールを統一すれば良いので、egressはそのままでも良いです。
provider "aws" { region = "ap-northeast-1" } resource "aws_security_group" "bastion" { (略) } resource "aws_security_group" "application" { name = "application" description = "Security Group for application" egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } } resource "aws_security_group_rule" "application_from_bastion" { type = "ingress" to_port = 22 protocol = "tcp" source_security_group_id = aws_security_group.bastion.id from_port = 22 security_group_id = aws_security_group.application.id } // aws_security_group_ruleとして新しく追加 resource "aws_security_group_rule" "application_from_http" { type = "ingress" to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] from_port = 80 security_group_id = aws_security_group.application.id }
これで何回 terraform plan
を実行しても謎の差分が出ることはなくなります。
一件落着です。
--
という話は公式にしっかりと書かれていました。
NOTE on Security Groups and Security Group Rules: Terraform currently provides both a standalone Security Group Rule resource (a single ingress or egress rule), and a Security Group resource with ingress and egress rules defined in-line. At this time you cannot use a Security Group with in-line rules in conjunction with any Security Group Rule resources. Doing so will cause a conflict of rule settings and will overwrite rules.
「aws_security_group_ruleを使う場合とaws_security_group内のingress/egressで定義する方法がありますが、同時に使うと競合するよ」みたいな感じでしょうか。
競合するので、どちらかに統一して書きましょうというお話でした。