FLINTERS Engineer's Blog

FLINTERSのエンジニアによる技術ブログ

tfmigrate を導入して、安心して terraform state が書き換えられるようになった

こんにちは。河内です。 これは FLINTERS advent calendar 2022 2日目の記事です。 2022年にやってよかったこととして、今日は tfmigrate の導入について書きます。

terraform でインフラを管理していて、しばらく経つと state を変更したくなる場面があります。例えば、一度手動で作ったリソースを import したいときとか、 .tf ファイルをリファクタリングしたいときとか、provider を更新するときとか。

terraform では terraform import や terraform state といったコマンドで state ファイルの編集ができます。 しかしこれらのコマンドは、手で実行するしかなく、レビューを受けることが難しく、履歴があとに残りません。

tfmigrate を使うと terraform import や terraform state コマンドをファイルとして記録し、Git で管理できるようになります。Git で管理できるということは、他のコードでやっているようにレビューを受けたり、CIで実行することが簡単にできるようになることを意味します。

導入したプロジェクトでは GitLab CI を用いているので、ここではそれ前提で話をします。

tfmigrate でどこまで適用したかは、ファイルに記録されます。ローカルファイルの他にS3とGCS上に記録するオプションがあります。 CIで実行する際には、S3やGCSを使うのが楽でしょう。 我々は terraform の state を S3 においていることを踏まえて S3 を選択しました。 このあたりの設定は .tfmigrate.hcl に書きます。書き方は tfmigrate の README をご参照ください。

我々は tfmigrate/ というディレクトリに tfmigrate 用の migration ファイルを置くと決め、MR *1tfmigrate/ 以下に変更があれば、CI で tfmigrate 用のパイプラインを実行するようにしました。

tfmigrate の設定 .tfmigrate.hcl は例えば次のようになります。

tfmigrate {
  migration_dir = "./tfmigrate"
  history {
    storage "s3" {
      bucket = "my-bucket"
      key    = "tfmigrate-history.json"
      region  = "ap-northeast-1"
    }
  }
}

GitLab CI の設定 .gitlab-ci.yml から該当部の雰囲気を抜き出すと次のようになります。

# tfmigrate plan を実行する CI job
tfmigrate-plan:
  # tfmigrate の入った image を指定
  image: my-tfmigrate-image
  rules:
    # default branch に対する MR でのみ実行
    - if: "$CI_PIPELINE_SOURCE != 'merge_request_event' && $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH"
      when: never
    # tfmigrate/ 以下に変更があるときのみ実行
    - changes:
       - tfmigrate/*
      when: on_success
  script:
    - tfmigrate plan

# tfmigrate apply を実行する CI job
tfmigrate-apply:
  image: my-tfmigrate-image
  needs:
    # tfmigrate-plan job が成功していたら実行できる
    - tfmigrate-plan
  rules:
    - if: "$CI_PIPELINE_SOURCE != 'merge_request_event' && $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH"
      when: never
    - changes:
       - tfmigrate/*
      # plan 結果を確認した後に job 実行は手動キック
      when: manual
  script:
    - tfmigrate apply

上記では記載していませんが、通常時に実行される terraform plan → terraform apply の job は、 tfmigrate/ 以下に変更がある場合には実行されないように rules を追加してあります。

以上で導入が完了です。以後 tfmigrate/ 以下にファイルを追加して、terraform state の変更がレビュー、CIから実行できるようになりました。 プロジェクトでは実際に AWS provider を version 4 に上げる 際に活用されたりしています。

導入した感想

tfmigrate plan は、内部で terraform plan を実行し、差分が出ないことを要求します。 これが tfmigrate の migration を書く際に大きな安心感をもたらすことを実感しました。 terraform plan で差分が出ないなら、state の書き換えは間違っていないと確信できます。

しかし現実ではうまくいくことばかりではなく、 terraform apply しても terraform plan で差分が出続けてしまうことがあるため、tfmigrate plan が通らずに困ってしまいました。 現状は、 lifecycle { ignore_changes = ... } で差分を一時的に無効化しておき、tfmigrate apply 後に無効化を取り消すという回避策を取っています。 terraform plan で差分が出続けるのはおそらく provider の問題の場合が多いように思うので、そのうち出なくなるといいなあ。

まとめ

terraform を使っているプロジェクトで tfmigrate を導入すると、state を自信をもって書き換えられるようになるよ。オススメ!

*1:MR: Merge Request。PR: Pull Request の GitLab 用語。merge を要求するので MR のほうが意味が通っているように思えるが、GitLab を使っていない人には PRと言ったほうが通じやすい。