ぶていのログでぶログ

思い出したが吉日

Terraformでlifecycleをoverrideする方法を考えた

こんにちは、最近Terraformデビューしました。 社内の人々がTerraformに苦しんでいたのをみてなんとなく敬遠していたのだけど、そうも言ってられなくデビューしたところうひょーーー!楽しい!!!ってなっている今日この頃。

lifecycleを動的に変更したい

そんなわけでTerraformを使い始め、すでに稼働しているサービスをテラフォーミングしはじめた。 サーバごとにresourceを定義するのだけど、同じような定義が多いのでモジュール化すると思う。 例えば以下のような感じ(説明に必要な最低限のものだけを書いている)。

❯ tree
.
├── a.tf
├── b.tf
└── modules
    └── base
        └── main.tf

❯ cat a.tf
module "a" {
  source = "modules/base"
-- snip --
}

❯ cat b.tf
module "b" {
  source = "modules/base"
-- snip --
}

❯ cat modules/base/main.tf
resource "openstack_compute_instance_v2" "base" {
-- snip --
  lifecycle {
     ignore_changes = [key_pairs]
  }
}

しかし、困ったことが起こった。 新たにc.tfを追加したいのだけど、このcサーバではSecurity Group(SG)をTerraformで管理したくない。 具体的には関門海峡を利用していて、SGが頻繁に変更される。 関門海峡でSGを追加しているときにTerraformを適用すると差分になってしまうのでこれをなんとかしたい。

dynamicを使う?

Terraformで差分として扱わないようにするためにlifecycleのignore_changesを指定すればよいのだけど、modules/base/main.tfにこの記述を追加するとa.tfやb.tfもSGが管理されなくなってしまう。 困った。

こういうときのためにdynamicが使えるのだが、なんとこのlifecycleにはdynamicは使えないのであった。

discuss.hashicorp.com

どうしたものか

overrideを使ってみた

解決策として、module/base/main.tfをコピーしてmodule/base_ignore_sg/main.tf を作れば良いと最初に考えた。 しかし、これだとbaseとbase_ignore_sgの2重管理になってめんどうだなっと思った。

そこでこれを解決するためにOverride Filesを使うことを考えた。 具体的には以下のような感じ

❯ tree
.
├── c.tf
└── modules
    ├── base
    │   └── main.tf
    └── base_ignore_sg
        ├── main.tf -> ../base/main.tf 👈 シンボリックリンク
        └── override.tf                👈

❯ cat c.tf
module "c" {
  source = "modules/base_ignore_sg" 👈
}

❯ cat modules/base_ignore_sg/override.tf
resource "openstack_compute_instance_v2" "base" {
  lifecycle {
     ignore_changes = [key_pairs, security_groups] 👈
  }
}

override.tfを追加して、この中にlifecycle/ignore_changesを追加してこの中でsecurity_groupsを追加している。 main.tfはbaseのシンボリックリンクなので二重管理にならない。 めでたしめでたし。

おわりに

これでやりたかったことはできた。 のだけど、tflintを流すとなぜかterraform_deprecated_interpolationでエラーになってしまう。 何が原因か調べていないけど、terraform validateでエラーにならないのでtflintで --disable-rule=terraform_deprecated_interpolation を指定することにした。

こんな感じで工夫してみたけど、もっといいやり方はあるのかな?