Terraformでリソースを破壊せず名称の変更やモジュール化をする

最近はTerraformを使ってコードでシステム構成を管理しています。
その中で名称を変更したくなったり、リソースをModule化する際にどうするのが良さそうか調べていたので覚書

背景

構築を進める中で、違う名前の方がよかったということに気づいたり、同じようなリソースを作るのでModule化したいということから既に構築済みのリソースを編集することがある。

環境変数を変えるとかメモリ量を上げるとかリソースの設定を変えた場合は、既存の構築物に対して変更がかかるが、名前を変えたりModule化して同名で利用する場合、リソースの 作り直し ということになる。

例えば、リソースの名称を変更し terraform plan を実行するとこうなる。

 # google_storage_bucket.bucket will be destroyed
  # (because google_storage_bucket.bucket is not in configuration)
  - resource "google_storage_bucket" "bucket" {
      - default_event_based_hold    = false -> null
      - effective_labels            = {} -> null
      - enable_object_retention     = false -> null
      - force_destroy               = false -> null
      - id                          = "BUCKET_NAME" -> null
      - labels                      = {} -> null
      - location                    = "US-WEST1" -> null
      ()
    }

  # google_storage_bucket.renamed_bucket will be created
  + resource "google_storage_bucket" "renamed_bucket" {
      + effective_labels            = (known after apply)
      + force_destroy               = false
      + id                          = (known after apply)
      + location                    = "US-WEST1"
      ()
    }

destroiedとcreatedが表示されている。

SecretManagerのような値とセットのものであればそこまで問題は無さそうだが、Cloud StorageやCloud SQLのような中身が別途存在するものだと都合が悪い。 (そもそもリソースの削除ができないかも)

こういった場合にどうしたらいいだろうかという話。

対処法

出てきたのは以下の2つ。

  1. movedブロックを使う
  2. terraform state mv を使う

1.movedブロックを使う

main.tfなんかに変更前後のリソースを書くという方法。

moved {
  from = google_storage_bucket.bucket
  to     = google_storage_bucket.renamed_bucket
}

from側に書くリソースはコード上に存在していなくても良い。
この状態で terraform plan を実行するとこうなる。

# google_storage_bucket.bucket has moved to google_storage_bucket.renamed_bucket
    resource "google_storage_bucket" "renamed_bucket" {
        id                          = "BUCKET_ID"
        name                        = "BUCKET_NAME"
        # (15 unchanged attributes hidden)

        # (1 unchanged block hidden)
    }

destroyedやcreatedの表示はなくこうなるよという内容のみ。

terraform applyの実行後はmovedブロックを消しておいていいようだが、消し忘れそうというのが個人的な感想。

terraformの操作をCI/CD上で行う場合は、このやり方になるかなと思う。

2. terraform state mv を使う

terraformのコマンドで、変更をstateに反映するという方法。以下の形式でコマンドを実行する。

$ terraform state mv 変更前  変更後

先程の例だとこうなる

$ terraform state mv google_storage_bucket.bucket google_storage_bucket.renamed_bucket

実行すると、確認などはなく結果が表示される

Move "google_storage_bucket.bucket" to "google_storage_bucket.renamed_bucket"
Successfully moved 1 object(s).

terraformのコマンドをローカルから実行する場合は、後から記述を消すような操作が必要なくこちらが楽かなという印象。

やってみて

どちらもやってみた結果、今の使い方的に terraform state mv でいいなと感じた。