TerraformのGoogleProviderのメジャーアップデートに対応するアップグレードとリカバリを理解して実施しよう

2024/09/28に公開

はじめに

最近は 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
}

1. $ terraform state pull でローカルに state ファイルをダウンロードしておく

こちらは何かあった時のために念の為に実施した方が良いかと思います。
ちなみに自分は $ 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 コマンドを実行します。

  1. $ terraform init -upgrade
    1. v5.44.1 の GoogleProvider のインストールが行われます
    2. プロバイダのバージョンなどを管理しているロックファイル .terraform.lock.hcl でもバージョンが更新されていることが確認できます
  2. $ terraform plan
  3. $ terraform apply
    1. plan で意図しない差分が発生していないかを確認してから実行しましょう

3. プロバイダを最新の v6.2.0 に上げる

これまでやってきた流れをメジャーバージョンをあげてもう一度行います。

  1. ソースコードの変更する
  2. $ terraform init -upgrade
  3. $ terraform plan
  4. $ 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 バックエンドでステートを過去のバージョンに戻す

GitHubで編集を提案

Discussion