1.この記事で達成したいこと

  • aws_security_groupリソースを使って複数のインバウンドルールを追加しようとして引いたエラーを解決する
$ terraform apply


│ Error: error updating Security Group (sg-000000000000): 
error authorizing Security Group (ingress) rules: 
InvalidParameterValue: The same permission must not appear multiple times
│ 	status code: 400, request id: 000000000000000000
│   with aws_security_group.dummy-sg,
│   on main.tf line 23, in resource "aws_security_group" "dummy-sg":
│   23: resource "aws_security_group" "dummy-sg" {
  • エラーメッセージが指す23行目付近で書いていること
$ view main.tf


 23 resource "aws_security_group" "dummy-sg" {
 24   name        = "dummy-sg"
 25   description = "dummy security group"
 26   vpc_id      = aws_vpc.dummy-vpc.id
 27 
 28   ingress {
 29     description      = "Allow 80 from anywhere for redirection"
 30     from_port        = 80
 31     to_port          = 80
 32     protocol         = "all"
 33     cidr_blocks      = ["0.0.0.0/0"]
 34   }
 35 
 36   ingress {
 37     description      = "Allow 8080 from anywhere for redirection"
 38     from_port        = 8080
 39     to_port          = 8080
 40     protocol         = "all"
 41     cidr_blocks      = ["0.0.0.0/0"]
 42   }

2.前提

  • Terraformのインストール方法を始めとする初期設定は終えているものとして話を進める

3.環境情報

  • Terraform実行環境
$ grep VERSION= /etc/os-release 
VERSION="20.04.3 LTS (Focal Fossa)"
$ terraform version
Terraform v1.1.0
on linux_amd64
+ provider registry.terraform.io/hashicorp/aws v3.70.0

※ terraform applyで使う要件については、後述するaws_security_groupリソースくらい。なので、ここでは割愛。

4.引いたエラーの再現方法

  • 以下のようなaws_security_groupリソースを作成した後、terraform apply
  • ポイントは以下の2点(aws_vpcリソースやaws_subnetリソースも書いているが、それらは適当でok)
    • aws_security_groupリソースにおいて適当なポートを開けること
    • 追加したルールのプロトコルは"all"や"-1"を指定すること
$ cat main.tf
resource "aws_vpc" "dummy-vpc" {
  cidr_block       = "192.168.0.0/16"
  instance_tenancy = "default"

  tags = {
    Name = "dummy"
  }
}


resource "aws_subnet" "dummy-subnet" {
  vpc_id = aws_vpc.dummy-vpc.id
  availability_zone = "ap-northeast-1a"
  cidr_block        = cidrsubnet(aws_vpc.dummy-vpc.cidr_block, 8, 0)

  tags = {
    Name = "dummy"
  }

}


resource "aws_security_group" "dummy-sg" {
  name        = "allow_tls"
  description = "Allow TLS inbound traffic"
  vpc_id      = aws_vpc.dummy-vpc.id

  ingress {
    description      = "Allow 80 from anywhere for redirection"
    from_port        = 80
    to_port          = 80
    protocol         = "all"
    cidr_blocks      = ["0.0.0.0/0"]
  }

  ingress {
    description      = "Allow 8080 from anywhere for redirection"
    from_port        = 8080
    to_port          = 8080
    protocol         = "all"
    cidr_blocks      = ["0.0.0.0/0"]
  }

  egress {
    from_port        = 0
    to_port          = 0
    protocol         = "-1"
    cidr_blocks      = ["0.0.0.0/0"]
  }

  tags = {
    Name = "dummy"
  }
}

5.原因

  • Terraformのドキュメントに書いてあった。該当箇所を抜粋する。

protocol - (Required) Protocol. If you select a protocol of -1 (semantically equivalent to all, which is not a valid value here), you must specify a from_port and to_port equal to 0. The supported values are defined in the IpProtocol argument on the IpPermission API reference.

出所:aws_security_group | Resources | hashicorp/aws | Terraform Registry

  • 本件の原因は、80番ポートや8080番ポートなど0ではない値をfrom_portあるいはto_portに投入しているにもかかわらず、protocolに"all"や"-1"を投入してしまったことと考えている

6.解決策

  • aws_security_groupリソースの protocol=“all” を修正
    • ここでは protocol=“tcp” と修正
$ git diff main.tf


     from_port        = 80
     to_port          = 80
-    protocol         = "all"
+    protocol         = "tcp"


     from_port        = 8080
     to_port          = 8080
-    protocol         = "all"
+    protocol         = "tcp"

※ なお、インバウンドルールの要件としてポートを全開放することを掲げているのであれば、アウトバウンドルール(egress)のようにprotocolは"all"ないし"-1"のままでもよいかもしれない。とはいえ、ベストな解決策とはいえないだろう。

  egress {
    from_port        = 0
    to_port          = 0
    protocol         = "-1"
    cidr_blocks      = ["0.0.0.0/0"]
  }

7.ちなみに開放するポートをひとつだけにして、protocol=“all"とした場合・・・

  • なんとapplyできてしまう
  • ただし、全開放となってしまっている (開けているポートは80番ポートだけではなくなってしまっている)
  ingress {
    description      = "Allow 80 from anywhere for redirection"
    from_port        = 80
    to_port          = 80
    protocol         = "all"
    cidr_blocks      = ["0.0.0.0/0"]
  }

  #ingress {
  #  description      = "Allow 8080 from anywhere for redirection"
  #  from_port        = 8080
  #  to_port          = 8080
  #  protocol         = "all"
  #  cidr_blocks      = ["0.0.0.0/0"]
  #}

  egress {
    from_port        = 0
    to_port          = 0
    protocol         = "-1"
    cidr_blocks      = ["0.0.0.0/0"]
  }
  • protocol=“tcp"と指定すればfrom_portとto_portで指定したポートのみ開放できていることが確認できた!

8.参考