しばやん雑記

Azure とメイドさんが大好きなフリーランスのプログラマーのブログ

非推奨となった Terraform リソースからのマイグレーションを安全に行いたい

Azure Provider に限った話ではないと思うのですが、正直 Azure Provider はリソースの非推奨化が多い気がするので書いています。きっかけとしては直近の Azure Provider の更新で azurerm_static_site は非推奨になって azurerm_static_web_app が追加されたことにあります。

追加された時から正直命名をミスっているなと思っていましたが、このバージョンから他の App Service に合わせる形で新しい名前のリソースが追加されました。機能としては変わっていないので、今回の場合は本当に名前が変わっただけです。

これまでにも Azure Provider では同じようなリソース変更があり、代表的かつ影響範囲の広いものとして App Service / Azure Functions があります。v3 のリリースタイミングで OS で別々のリソースが用意されるようになり、これまでのリソースは v4 で削除されるためマイグレーションが必須です。

しかし、そのリソースを移行すると言っても Terraform 側でのサポートがあるわけではなく、単純にリソースを変更すると再作成が走ってしまうため tfstate から削除してインポートし直す必要があります。具体的な手順は以下のマイグレーションのドキュメントにありますが、本番環境の tfstate を CLI から触るというのは危険な作業には変わりありません。

折角 Terraform の運用を GitHub Actions や Terraform Cloud で自動化していても、たまにあるマイグレーションで手動作業になってしまうのは避けたいので、CI/CD を生かしたまま実行したいところです。

そこで最新の Terraform では removed ブロックと import ブロックが使えるようになっているので、これを使ってマイグレーションを簡単に行えないか実際のリソースで試しました。Terraform CLI を使った作業であれば tfstate から削除してインポートし直すという 2 つの手順が必要ですが、この removedimport を使うと理屈上は 1 回で終わるはずです。

実際に azurerm_static_web_app へのマイグレーションを行った時の Terraform 定義は以下のようになります。元々あった azurerm_static_site は削除しつつ同時に removed を追加して、該当リソースを tfstate から削除します。そして azurerm_static_web_appimport を追加することで新しいリソースとしてインポートするようにしています。

この時 removed には明示的に destroy = false を追加してリソース自体は削除しないようにします。

#resource "azurerm_static_site" "default" {
#  name                = "stapp-xxxxxx"
#  location            = var.location
#  resource_group_name = var.resource_group_name
#}

removed {
  from = azurerm_static_site.default

  lifecycle {
    destroy = false
  }
}

resource "azurerm_static_web_app" "default" {
  name                = "stapp-xxxxxx"
  location            = var.location
  resource_group_name = var.resource_group_name
}

import {
  id = "/subscriptions/.../resourceGroups/.../providers/Microsoft.Web/staticSites/..."
  to = azurerm_static_web_app.default
}

本来であれば import で指定する id を既存のリソースから取得したいのですが、removed ブロックを使う場合はリソース定義は削除する必要があるので無理でした。更に import ブロックはモジュールの中では書けないようなので、ルートで指定する必要があるのも面倒でした。将来的には改善して欲しいです。

このように書くことで古いリソースの tfstate からの削除と、新しいリソースのインポートが 1 回で完了します。実際に稼働しているアプリケーションで移行を行った Pull Request が以下のようになります。

カスタムドメインも移行しているので 2 つ分 removedimport を書いて対応しています。現在の Azure Provider バージョンではカスタムドメインのインポートに失敗している気がするので、再作成した方が結果として安定する予感です。

Terraform Cloud で運用しているので plan 結果を確認すると tfstate からの削除とインポートが行われていることが分かります。カスタムドメインは若干この時点でも挙動がおかしいです。

この Pull Request をマージすると apply が走って新しいリソースへのマイグレーションが完了します。Terraform CLI で tfstate を直接触るよりも安全かつ効率的に行えました。

最後に追加した removed ブロックと import ブロックを削除する Pull Request を作成します。

この Pull Request の plan を確認すると変更なし扱いとなるので、今回の removedimport を使ったマイグレーションが正しく行われていることが確認出来ました。

最新の Terraform を使っておく必要がありますが、Terraform CLI を使って手動で作業するよりも格段に安全なので、今後も Azure Provider でリソースの非推奨化が行われても安心して作業が出来そうです。