Terraform

Terraformのstate移動とrename

Terraformといえばstate管理

Bicepと違ってstate管理必須なTerraform先輩

業務で使うのがもっぱらAzureというのもあって、これまでstate管理のいらないBicep先生をメインで使ってきたわけですが、Terraformから逃げているわけにもいかず最近少しずつ始めています。

こういうのってまずは小規模かつモノリシックにはじめるワケですが、Terraformの場合わりとすぐにDRYにしたくなったり、ライフサイクルの違いなどからスコープを分離したくなったり、といった壁にぶち当たります。

てなわけで、モノリシックなstateを分離する方法を忘れないうちに書くことにしました。

参考記事

あまりやりたいことをそのものズバリで書いている記事が見つけられませんでしたが、以下の2記事を参考にしました。

今回の説明範囲

これまで同一のstateファイルで管理していたリソース群から、一部だけを別のstateファイルへ切り出す方法について説明します。

下記2通りがありますが、両方とも説明します。

  1. ファイルAをファイルA1とファイルA2に分けたいパターン
  2. ファイルAの一部をファイルBに移動させたいパターン

また、単純にstateのリソース名を変更したいケースの説明も含みます。

手順

①stateをリモートからローカルに一時的に変更する

チーム開発でIaCファイルを管理する場合、tfファイルを管理するリポジトリとは別に、秘匿情報の含まれるstateファイルをクラウドストレージに載せてリモートで管理していることがほとんどだと思います。

この場合、リモートのstateはリポジトリのmainに準拠している状態が正となります。

これから実行する切り離し作業によって、まだmainにmergeされていない変更がリモートに影響を及ぼすことを避けるため、まずはstate管理をリモートからローカルへ一時的に変更します。

具体的には、tfファイルのbackend情報をoverrideするのがよいでしょう。

※Terraformでは*_override.tfあるいはoverride.tfという名称のファイルは、実行時に上書き情報として処理されるようになっています。

また、リモートからローカルへのstate移動は以下2通りの方法が考えられます

-reconfigureを使用する方法

まず、リモートのstateファイルをpullします。

terraform state pull > terraform.tfstate

その後、backendをリモートからローカルに変更します。 元ファイルを書き換えずに実行するための仕組みが上述のoverride機能です。

cat << EOF > override.tf
terraform {
  backend "local" {
    path = "terraform.tfstate"
  }
}
EOF

override用のファイルが無事に作成出来たら、変更を適用します。

terraform init -reconfigure

上記の-reconfigureフラグにより、pullしてきたstateファイルを変更せずにそのまま使う形でinitされます。

-migrate-stateを使用する方法

こちらはpullの作業を自動で実行させる形になります。まず、override.tfについては上記と同様に作成してください。

その後、リモートのstateをそのまま引き継ぐ形で変更を適用します。

terraform init -migrate-state

その後、プロンプトで実行してよいか聞かれるため、yesを入力します。具体的には下記のようなプロンプトです。

Initializing the backend...
Backend configuration changed!

Terraform has detected that the configuration specified for the backend
has changed. Terraform will now check for existing state in the backends.


Terraform detected that the backend type changed from "azurerm" to "local".
Do you want to copy existing state to the new backend?
  Pre-existing state was found while migrating the previous "azurerm" backend to the
  newly configured "local" backend. No existing state was found in the newly
  configured "local" backend. Do you want to copy this state to the new "local"
  backend? Enter "yes" to copy and "no" to start with an empty state.

  Enter a value: yes

ローカルへの移動が終わったら、terraform planを叩いて、差分が生じていないことを確認しておきましょう。

②ローカルのstateファイルからstate mvする

現在のstateファイルに含まれている管理対象を以下のコマンドで確認できます。

terraform state list

ここから一部のstateを移動します。移動先は仮にtmp.tfstateとしましょう。

terraform state mv -state-out=tmp.tfstate \
<target_identifier_before> <target_identifier_after>

この際、移動と同時に対象のidentifierを変更することもできます
変更しない場合は、<target_identifier_before><target_identifier_after>を同一にしてください。

また、stateの移動はさせずに同一ファイル内で単にrenameしたい場合は、-state-out=tmp.tfstateを外して実行します。

※対象のidentifierを変更した場合は、tfファイルから対応するリソースの識別名を変更するのを忘れないでください。

例) 旧:resource "azurerm_application_gateway" "appgw-old" { 新:resource "azurerm_application_gateway" "appgw-new" {

ファイルAをファイルA1とファイルA2に分けたいパターン

stateの移動に関しては、この時点で終わりです。

stateが期待通り移動していることをterraform state listで確認しておくのがよいでしょう。

ファイルAの一部をファイルBに移動させたいパターン

すでに紹介した手順の通りにファイルBのstateをリモートからローカルに移動してください。

次に、作成したtmp.tfstateからファイルBのterraform.tfstateへ再度stateを移動します。

terraform state mv -state=tmp.tfstate -state-out=terraform.tfstate \
<target_identifier_before> <target_identifier_after>

③変更をリモートに反映する

tfコードの変更がmainブランチにマージされたら、リモートのstateにも変更を反映します。

まず、override.tfを削除します。

rm override.tf

その後、backendをリモートに再変更します。

terraform init -migrate-state

参考

以下の方法は推奨されませんが、手動介入が必要になった場合に使用できます。
-migrate-stateを使わずに実行する方法になります。

その場合は、まず元のリモートにあるstateを参照している状態に戻します。

terraform init -reconfigure

その後、ローカルのstateをpushします。

terraform state push terraform.tfstate

エラーチェックで弾かれる場合、--forceフラグを使用できますが、非推奨です。

terraform state push --force terraform.tfstate

④編集したtfファイルを用いて差分を確認する

stateファイルの移動が終わったら、tfファイル側を編集してterraform planします。

想定通りstateの移動とtfファイルの編集ができていれば差分は発生しません。

おわりに

以上でstateの移動についての説明は終わりです。