📝

TerraformでAWSのセキュリティグループを書く場合に気を付けること その1

2023/04/10に公開

こんにちは。
TerraformでAWSのセキュリティグループの設定を書くときに注意すべきことがあるのでそれを忘備録的に記載します。

・aws_security_groupリソースで定義した後、aws_security_group_ruleリソースでルールを追加しない。

下記の公式のドキュメントにも書かれているのですが、競合が発生するためplan、applyを実行するたびにTerraformでは変更があったと認識され毎回ルールが上書きされます。
https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group

具体的な例としては下記のようなaws_security_groupリソースを定義し

terraform {
  required_version = "~> 1.4.0"
}
provider "aws" {}

resource "aws_vpc" "default" {
  cidr_block = "19.168.128.0/24"
  tags = {
    group = "sgtest"
  }
}

resource "aws_security_group" "web" {
  vpc_id = aws_vpc.default.id
  name   = "web-sg"
  ingress {
    from_port = 80
    to_port   = 80
    protocol  = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
   group = "sgtest"
 }
}



下記のようにaws_security_group_ruleリソースを追加した場合に発生します。

terraform {
  required_version = "~> 1.4.0"
}
provider "aws" {}

resource "aws_vpc" "default" {
  cidr_block = "19.168.128.0/24"
  tags = {
    group = "sgtest"
  }
}

resource "aws_security_group" "web" {
  vpc_id = aws_vpc.default.id
  name   = "web-sg"
  ingress {
    from_port = 80
    to_port   = 80
    protocol  = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
   group = "sgtest"
 }
}

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       = ["0.0.0.0/0"]
} 

aws_security_group_ruleリソースを追加した直後にplanをとるとchangeが発生しています。

hogehoge@LAPTOP:~/src/changetest$ terraform plan
aws_vpc.default: Refreshing state... [id=vpc-xxxxxxxxxxxxxx]
aws_security_group.web: Refreshing state... [id=sg-xxxxxxxxxxxxxx]
aws_security_group_rule.web_https: Refreshing state... [id=sgrule-xxxxxxxxxxxxxx]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # aws_security_group.web will be updated in-place
  ~ resource "aws_security_group" "web" {
        id                     = "sg-xxxxxxxxxxxxxx"
      ~ ingress                = [
          - {
              - cidr_blocks      = [
                  - "0.0.0.0/0",
                ]
              - description      = ""
              - from_port        = 443
              - ipv6_cidr_blocks = []
              - prefix_list_ids  = []
              - protocol         = "tcp"
              - security_groups  = []
              - self             = false
              - to_port          = 443
            },
          - {
              - cidr_blocks      = [
                  - "0.0.0.0/0",
                ]
              - description      = ""
              - from_port        = 80
              - ipv6_cidr_blocks = []
              - prefix_list_ids  = []
              - protocol         = "tcp"
              - security_groups  = []
              - self             = false
              - to_port          = 80
            },
          + {
              + cidr_blocks      = [
                  + "0.0.0.0/0",
                ]
              + from_port        = 80
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "tcp"
              + security_groups  = []
              + self             = false
              + to_port          = 80
            },
        ]
        name                   = "web-sg"
        tags                   = {
            "group" = "sgtest"
        }
        # (7 unchanged attributes hidden)
    }

Plan: 0 to add, 1 to change, 0 to destroy.

解説

aws_security_groupリソースの状態はingss 80番ポートの設定のみですがAWS上の設定ではingress 80ポートと443ポートで食い違いがあるため変更の対象として検出されingress 443ポートは削除の対象とされています。
aws_security_group_ruleリソースの状態とAWS上の実際の設定は同じなので変更の対象として検出されていません。

これをapplyし、planをとった場合はingress 443ポートは削除されているためaws_security_groupリソースは変更の対象となりませんが、aws_security_group_ruleリソースではingress 443ポートはないため追加の対象となります。

よって毎回applyするたびにingress 443ポートが追加されたり、削除されたりという動きになります。

まとめ

ルールを書くときはaws_security_group_ruleリソースに書くようにし、aws_security_groupリソースはaws_security_group_ruleリソースを定義するためのガワとして書くようにしましょう。

レスキューナウテックブログ

Discussion