Snowflake Terraform ProviderのVersion Upをなるべく安全にやる
Snowflake Providerについて
先日、Snowflake Providerのv1リリースが12/9に予定されていることが以下のドキュメントで発表されました。
現在の最新versionはv0.99.0で、Snowflake Providerはv1の正式リリースに向けて再設計を進めてきました。
頻繁にリリースされていて、さらに多くのversionでbreaking changeが含まれています。そのため何かしらの対応が必要なことがほとんどです。
安易にversionを上げてそのままapplyをしてしまうと、データ基盤に予期せぬ影響を与えてしまうので、なるべくSnowflakeに対して変更が走らないようにversion upを行うことが理想です。
この記事では私がどのようにversion upを行なっているかを紹介します。
Version upの手順
基本的にversionは一つずつ上げていくことをおすすめします。公式もそのようなやり方を推奨しています。
-
renovateがproviderの最新versionがリリースされたことを検知してPRをあげてくれます
- providerの使用箇所が多い場合はrenovateを使って管理するのが楽です
- renovateの設定
-
{ packageRules:[ { groupName: "terraform snowflake dependencies", groupSlug: "terraform-snowflake-dependencies", matchManagers: ["terraform"], matchPackageNames: ["snowflake-labs/snowflake"], }, ] }
- snowflake providerはCIが通ったらマージすればオッケーというわけではなく、対応が必要なことが多いので、snowflakeだけで一個のPRになるようにしています
- release noteを読んで内容をざっくり把握します
- migration guideを読んでどのリソースが対象でどのようなbreaking changeがあり、どういう対応が必要かを確認します
- 実際にplanしてみてplan結果にどのようなerror、warningが出ているのか確認します
- ここで問題なければversion upは完了です
- breaking changeやdeprecated resourceの移行に対応します
必要な対応
breaking change
- できる限りplan結果に出るdiffを減らすことを目指します
- プロパティが追加されている場合、default値が変わった場合は今設定されている値を確認して、指定するようにします
- どうしてもdiffが生じてしまう場合は、変更が走っても良い一部のリソースでお試しapplyしてみます
- apply時にtargetを指定するとやりやすいです
- 走るクエリを確認して問題なさそうなのかを確認してから全体でapplyします
0.94.0 -> 0.95.0の例
before
~ resource "snowflake_user" "user" {
+ disable_mfa = "default"
~ disabled = "false" -> "default"
- display_name = (sensitive value) -> null
id = "id"
+ mins_to_bypass_mfa = -1
+ mins_to_unlock = -1
+ must_change_password = "default"
# Warning: this attribute value will no longer be marked as sensitive
# after applying this change. The value is unchanged.
~ name = (sensitive value)
~ show_output = [
- {
...
},
] -> (known after apply)
# (70 unchanged attributes hidden)
}
今回変更があったプロパティに値を設定した
resource "snowflake_user" "user" {
name = upper(var.name)
login_name = local.login_name
display_name = var.display_name
email = var.email
default_role = local.default_role
default_warehouse = local.default_warehouse
default_namespace = local.default_namespace
disabled = false
must_change_password = false
}
after
~ resource "snowflake_user" "user" {
+ disable_mfa = "default"
# Warning: this attribute value will no longer be marked as sensitive
# after applying this change.
~ display_name = (sensitive value)
id = "id"
+ mins_to_bypass_mfa = -1
+ mins_to_unlock = -1
# Warning: this attribute value will no longer be marked as sensitive
# after applying this change. The value is unchanged.
~ name = (sensitive value)
~ show_output = [
- {
...
},
] -> (known after apply)
# (72 unchanged attributes hidden)
}
この状態であまり影響が出ても問題ない一つのユーザーに対してapplyしたら以下のクエリが走りました。
ALTER USER "USER_NAME" UNSET MUST_CHANGE_PASSWORD, MINS_TO_UNLOCK, MINS_TO_BYPASS_MFA, DISABLE_MFA
このSQLがユーザーに対して走ることは問題なさそうだったら全部まとめてapplyします。
deprecated resource
removed block, import blockを使ってresourceを移行していきます
snowflake_role
-> snowflake_account_role
への移行例
terraform側での状態の持ち方が変わっただけなので、古いresourceでの状態を削除して、新しいresourceで状態を持つようにimportするイメージです。
before
resource "snowflake_account_role" "this" {
name = "ROLE_NAME"
}
after
resource "snowflake_account_role" "this" {
name = "ROLE_NAME"
}
removed {
from = snowflake_role.this
lifecycle {
destroy = false
}
}
import {
to = snowflake_account_role.this
id = "ROLE_NAME"
}
plan結果のdiffは以下のようになります
. resource "snowflake_role" "this" {
id = "ROLE_NAME"
name = "ROLE_NAME"
# (3 unchanged attributes hidden)
}
Plan: 103 to import, 0 to add, 0 to change, 0 to destroy.
実際には多くのresourceが定義されていて、一つ一つこのようなblockを書いていくのはとても大変です。生成AIを使って作業を簡略化することができます。
自分の環境では多くのresourceはmodule内で定義されています。
そのためremoved blockはmodule内部に記述すれば良いのですが、import blockはmoduleを呼び出している箇所に記述しなければならないのでimport blockをresourceの数だけ記述する必要があります。
import対象のresourceが定義されているコードと一緒に以下のプロンプトを使って生成してもらっています。
このコードはsnowflakeのroleをterraformで定義しています。上で定義されているresourceをimport blockを使ってimportしたいです。resourceの数だけ以下のフォーマットに従ってimport blockを生成してください
```tf
import {
to = snowflake_account_role.{name}
id = "{ROLE_NAME}"
}
```
なんかうまくいかないなと思ったら
v1リリースに向けて開発をガンガン進めていた状態なので、バグに遭遇することは少なくありません。
そのような時はissueを立てましょう
細かく情報を書いたらすぐに反応してくれることが多いです。
Discussion