FLINTERS Engineer's Blog

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

この度、ブログを新たに開設いたしました。今後の記事は、すべて新しいサイトで公開してまいります。

https://zenn.dev/flinters_blog

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と言ったほうが通じやすい。