TerraformのGoogleProviderのメジャーアップデートに対応するアップグレードとリカバリを理解して実施しよう
はじめに
最近は Terraform を触る機会がぼちぼち増えてきました。(色々アウトプットしていきたい)
そんな中、少し前にあった Terraform の GoogleProvider のメジャーアップデートに伴い GoogleProvider のアップグレードを実施したので安全に進める方法やリカバリ方法などについて行なったことを共有したいと思います。
今回は google-provider v5.39.1 から v6.2.0 へのアップグレードを行いました。
事前情報
Terraform: v1.9.3
google-provider: v5.39.1
Backend(remote): GoogleCloud Storage
記事を読んでわかること
- プロバイダのバージョンをアップグレードを進め方
- upgrade オプションについて
- リカバリ方法
アップグレードの進め方
それでは本題です。
基本的には Terraform Google Provider 6.0.0 Upgrade Guide に沿って行いました。
一つずつ見ていきたいと思います。
ちなみにアップグレード前のソースコードは下記になります(該当箇所のみ)
Terraform コマンドはこの main.tf ファイルがあるディレクトリで実行します。
※バージョンの指定方法については各チームなどで変わると思います。
https://developer.hashicorp.com/terraform/language/expressions/version-constraints
// main.tf
terraform {
required_version = "1.9.3"
required_providers {
google = {
source = "hashicorp/google"
version = "5.39.1"
}
}
}
terraform {
backend "gcs" {
bucket = "xxxxxxxx" // バケット名
prefix = "xxxxxxxx" // プレフィックス
}
}
provider "google-beta" {
user_project_override = true
region = var.region
}
$ terraform state pull
でローカルに state ファイルをダウンロードしておく
1. こちらは何かあった時のために念の為に実施した方が良いかと思います。
ちなみに自分は $ terraform state pull > backup.tfstate
でローカルに state ファイルをダウンロードしました。
※state ファイルには機密情報も含まれているため取り扱いには十分気をつける必要があります。一通りの作業が完了したら削除を忘れないようにしましょう。
2. バージョンを v5 の最新系に更新する
まずはいきなりメジャーアップデートを行うのではなく、現在の v5 をパッチバージョン含めて一番最新のものにします。
GoogleProvider の v5 の最新系は v5.44.1 なので、terraform ブロックを次のように修正します。
terraform {
required_version = "1.9.3"
required_providers {
google = {
source = "hashicorp/google"
- version = "5.39.1"
+ version = "5.44.1"
}
}
}
terraform {
backend "gcs" {
bucket = "xxxxxxxx" // バケット名
prefix = "xxxxxxxx" // プレフィックス
}
}
provider "google-beta" {
user_project_override = true
region = var.region
}
コードを修正したら次の順番で Terraform コマンドを実行します。
-
$ terraform init -upgrade
- v5.44.1 の GoogleProvider のインストールが行われます
- プロバイダのバージョンなどを管理しているロックファイル
.terraform.lock.hcl
でもバージョンが更新されていることが確認できます
$ terraform plan
-
$ terraform apply
- plan で意図しない差分が発生していないかを確認してから実行しましょう
3. プロバイダを最新の v6.2.0 に上げる
これまでやってきた流れをメジャーバージョンをあげてもう一度行います。
- ソースコードの変更する
$ terraform init -upgrade
$ terraform plan
$ terraform apply
google-provider v6.2.0 に変更する
terraform {
required_version = "1.9.3"
required_providers {
google = {
source = "hashicorp/google"
- version = "5.44.1"
+ version = "6.2.0"
}
}
}
terraform {
backend "gcs" {
bucket = "xxxxxxxx" // バケット名
prefix = "xxxxxxxx" // プレフィックス
}
}
provider "google-beta" {
user_project_override = true
region = var.region
}
意図しない差分が発生した時のリカバリについて
意図しない差分等が発生した場合のリカバリについては $ terraform apply
を実行しているかどうかで対応が分かれます。
apply していない場合
まだ apply を実行していない場合は state ファイルや実際のリソースに影響は及んでいないので、ソースコードの google-provider のバージョンを元に戻してから $ terraform init -upgrade
を実行すれば大丈夫です。
terraform {
required_version = "1.9.3"
required_providers {
google = {
source = "hashicorp/google"
- version = "6.2.0"
+ version = "5.39.1"
}
}
}
terraform {
backend "gcs" {
bucket = "xxxxxxxx" // バケット名
prefix = "xxxxxxxx" // プレフィックス
}
}
provider "google-beta" {
user_project_override = true
region = var.region
}
apply してリソースに変更があった場合
次に apply を実行している場合の対応についてですが、実際のリソースに影響が出ている状態なのでアップグレードする前のリソースの状態に戻す必要があります。
そのための方法として最初にローカルへダウンロードしておいた backup.tfstate
ファイルを使った対応をみていきます。
今回はアップグレード前のリソースに戻すためのコマンドとして $ terraform state push
を使います。
まずは普通に $ terraform state push backup.tfstate
を実行してみます。
すると次のようなエラーが出ると思います。
Failed to write state: cannot import state with serial 2 over newer state with serial 3
これは state ファイルの serial が過去に戻っているのが原因です(この場合は 3 から 2)
{
"version": 4,
"terraform_version": "1.9.3",
+ "serial": 2,
- "serial": 3,
// 以下省略
}
serial は 0 から始まり state ファイルが更新されるたびに増えていくようになっているので、serial の番号が小さくなるということは過去の状態に戻ることを意味しています。
今回は意図して過去の状態に戻したいので、このエラーが出ることは想定内です。
逆に気をつけたいのは serial ではなく lineage が一致しない場合です。
Failed to write state: cannot import state with lineage xxxxx over newer state with serial xxxxx
{
"version": 4,
"terraform_version": "1.9.3",
"serial": 2,
+ "lineage": "d8740741-c6cc-e3de-3e15-b956dc1f2be3",
- "lineage": "xxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx",
// 以下省略
}
lineage は UUID でステートが作られたときに生成され、以降不変の値になります。
そのため lineage が違う場合は別のステートを指している可能性があります。ステートの場所が間違っていないか確認しましょう。
ここまでで push した時のエラーが serial であると分かった場合は force オプションをつけて再度実行してみましょう。
(lineage の場合は上記の通りなので force オプションで push しないようにしましょう)
force オプションは serial と lineage のチェックをスキップしてくれるので先ほどのエラーは発生せずに実行できます。
$ terraform state push -force backup.tfstate
$ terraform init
について理解する
せっかくなので、$ terraform init
について少しだけ深ぼってみていきたいと思います。
$ terraform init
にはいくかの機能がありますが、今回はプロバイダに関する話なのでプロバイダのインストールについてみていきます。
まずそもそも Terraform CLI を提供している Terraform 自体(Terraform Core)は Google や AWS などのプロバイダの情報は持っていません。
Terraform にはプラグインという拡張機能があり Google や AWS、Azure といったプロバイダをサポートしています。
このプロバイダが各プラットフォームに対して API を使ってリソースの操作をするため、指定されたプロバイダをインストールすることで、Terraform 経由で各プラットフォームのリソースを操作できます。
※インストールの元の場所は Terraform Registry などの外部のレジストリになります。
では Terraform はどうやってインストールするプロパイダを判別しているのでしょうか。
それは tf ファイル内にある required_providersブロック
を読み取ることで判別しています。
source はインストールするアドレスを指しており、この場合は Terraform Registry の hashicorp 配下にある google というプロバイダの 5.39.1 バージョンをインストールするように指定しています。
terraform {
required_version = "1.9.3"
required_providers {
google = {
source = "hashicorp/google"
version = "5.39.1"
}
}
}
大まかではあるのですが、このような流れで $ terraform init
でプロバイダのインストールを行っています。
upgrade オプションとは
次に upgrade オプションについても少しみていきたいと思います。
そもそも init が成功した時に .terraform.lock.hcl
というロックファイルが作成されます。これは使用するプロバイダのバージョンやハッシュ値などの情報を持ったファイルです。
このファイルがあることで、以降の init では全く同じプロバイダのバージョンをインストールします。そのため Git で管理することで、チームで同じバージョンのプロバイダを使用することが保証できます。
upgrade オプションはこのロックファイルを無視してプロバイダのインストールが行われます。
そのため今回のプロバイダのアップグレードなどの要件で活躍してくれるオプションとなっています。
Google Provider v6 で追加された機能
この記事では特に話題にはしませんが、変更点についてはアップグレードガイドをご確認ください。
個人的には google-project のデフォルトの設定が PREVENT になっているのは良い点だと思いました。
この設定により、Terraform 経由で GoogleCloud のプロジェクトをうっかり削除してしまうことを防ぐことができるためです。(GUI 上からの操作であれば削除可能)
参考記事
How Terraform Works With Plugins
Terraform Google Provider 6.0.0 Upgrade Guide
Terraform Google Provider 6.0.0 の新機能と変更点について
Terraform state 概論
Terraform + S3 バックエンドでステートを過去のバージョンに戻す
Discussion