LoginSignup
11
2

More than 1 year has passed since last update.

TerraformでEC2を作った後に、security_groupsの指定方法でインスタンスの再作成になってしまった話

Last updated at Posted at 2020-06-18

What's?

Terraformを使ってEC2を立てる時に、条件によってデフォルトのセキュリティグループを作る・作らないみたいな制御を入れていたら、妙な挙動をされたので困ったという話。

結論としては、EC2の定義に指定していたセキュリティグループの指定に使う属性が誤っていたのですが。

やりたかったこと

こんな感じに、変数でセキュリティグループを与えたら指定のものを使い、そうでなければデフォルトのセキュリティグループを作成する、みたいなことをやろうとしました。

variable "my_security_groups" {
  type    = list(string)
  default = []
}

resource "aws_security_group" "this" {
  count = length(var.my_security_groups) > 0 ? 0 : 1

  ...
}

resource "aws_instance" "this" {
  ami           = "..."
  instance_type = "..."
  subnet_id     = "..."

  security_groups = length(var.my_security_groups) > 0 ? var.my_security_groups : [aws_security_group.this[0].id]
}

まあ、ハマりましたと。

環境

今回の環境は、こちらです。

$ terraform version
Terraform v0.12.26
+ provider.aws v2.66.0

AWS Providerの指定とVPCの作成

VPCの作成までは、AWS VPC Terraform moduleを使ってさっくりと。

AWS VPC Terraform module

terraform {
  required_version = "0.12.26"
}

provider "aws" {
  version = "2.66.0"
}

module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "2.39.0"

  name = "my-vpc"
  cidr = "10.0.0.0/16"

  azs            = ["ap-northeast-1a", "ap-northeast-1c"]
  public_subnets = ["10.0.1.0/24", "10.0.2.0/24"]

  map_public_ip_on_launch = true

  enable_nat_gateway = false
}

EC2+セキュリティグループ

こんな感じで、セキュリティグループをInput Variableとして定義し、指定されていればEC2のセキュリティグループに設定し、そうでなければデフォルトのセキュリティグループを作って指定するように定義してみました。

variable "my_security_groups" {
  type    = list(string)
  default = []
}

resource "aws_security_group" "this" {
  count = length(var.my_security_groups) > 0 ? 0 : 1

  name   = "my-ec2-instance-sg"
  vpc_id = module.vpc.vpc_id

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

resource "aws_instance" "this" {
  ami           = "ami-0a1c2ec61571737db"
  instance_type = "t2.micro"
  subnet_id     = module.vpc.public_subnets[0]

  security_groups = length(var.my_security_groups) > 0 ? var.my_security_groups : [aws_security_group.this[0].id]
}

※ アウトバウンドしか許可していませんが、今回は簡単に済ませています

で、apply。

$ terraform apply

リソースの作成は、とりあえずうまくいきます。今回はInput Variableに値を指定していないので、デフォルトのセキュリティグループ、つまりこちらが作成されてEC2に割り当てられます。

resource "aws_security_group" "this" {
  count = length(var.my_security_groups) > 0 ? 0 : 1

  name   = "my-ec2-instance-sg"
  vpc_id = module.vpc.vpc_id

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

ここで、そのままplanを実行してみます。

$ terraform plan

なんと、EC2の再作成が提示されます。

------------------------------------------------------------------------

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
-/+ destroy and then create replacement

Terraform will perform the following actions:

  # aws_instance.this must be replaced
-/+ resource "aws_instance" "this" {

セキュリティグループが再作成の理由みたいです。

      ~ security_groups              = [ # forces replacement
          + "sg-0f3490bf5526991db",
        ]

実際、このままapplyするとEC2が再作成されてしまいます。

ちなみに、セキュリティグループ自体はなにも変わりませんし、再作成もされません

この指定方法がいけないのかな?と思い

  security_groups = length(var.my_security_groups) > 0 ? var.my_security_groups : [aws_security_group.this[0].id]

指定方法を変えたり

  security_groups = length(var.my_security_groups) > 0 ? var.my_security_groups : aws_security_group.this.*.id

セキュリティグループだけ別モジュールに切り出したり、変数の定義方法を変えたり、ループをcountではなくfor_eachに変えてみたりしたのですが、やっぱり再作成になってしまいます。

で、いろいろ見ていたらこんな記事を見つけたので

Terraformで立てたEC2に後からSGを追加しようとするとEC2が再作成される

なるほど、security_groupsを使っているのがダメなんだということで、vpc_security_group_idsにすると差分が出なくなりました。

  vpc_security_group_ids = length(var.my_security_groups) > 0 ? var.my_security_groups : [aws_security_group.this[0].id]
  #vpc_security_group_ids = length(var.my_security_groups) > 0 ? var.my_security_groups : aws_security_group.this.*.id  # こちらも可

デフォルトのVPCを使っているわけではないので、これで大丈夫ですね。

めでたし、めでたし。

本当に?

EC2でセキュリティグループを指定する際に、security_groupsからvpc_security_group_idsへ変更することで、この挙動を回避できたわけですが。

security_groupsvpc_security_group_idsの定義の差を見比べてみると、ForceNewしか違いがありません。

差分があったら、リソースの再作成を強制するみたいですね。

ところで、今回のケースだと、問題になっているセキュリティグループ自体には差分がありません。

にも関わらず、EC2の再作成を強制させられます

実際、security_groupsを使っている時に、terraform applyでEC2を再作成してもセキュリティグループの定義や、EC2に割り当てているセキュリティグループの情報はterraform.tfstateを見ても差がないんですよね。

ForceNewで定義されている引数に対して、こういう条件指定したりすると、危ないかも?という話なんでしょうかね(リストで指定するのも微妙なのかなぁという気もしますが…)。

どっちにしろ、リソース作成後もちゃんとterraform planをして、妙な動きをしないか確認しようという気になりました…。

11
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
2