最近よくデータ基盤構築の仕事をしているため、TerraformでGCPをいじったりしています。今回はCloud Storageのバケット名を変更する際に罠にはまりやすいところを紹介します。もちろん、Cloud Storageだけでなく、他のサービスやAWSなどにも活用できそうな箇所があるではないかと思います。

環境

Terraform v1.1.2
on darwin_arm64
provider registry.terraform.io/hashicorp/google v4.15.0

他の環境は正しく動作するかは未検証です。

経緯

下記のバケットを作って検証環境に反映した後、PRを出しました。

resource "google_storage_bucket" "my_bucket" {
  name                        = "my-bucket"
  storage_class               = var.gcs_storage_class.coldline
  project                     = var.project_id
  location                    = var.gcs_location
  force_destroy               = false
  uniform_bucket_level_access = true

  retention_policy {
    is_locked        = true
    retention_period = 30
  }



  lifecycle_rule {
    condition {
      age = 30
    }
    action {
      type = "Delete"
    }
  }
}

チームメンバーのレビュー受けて、バケット名はmy-bucketよりmy-bucket-hogeの方が良いと言われたので、my-bucketmy-bucket-hoge変更しterraform applyしたらエラーになりました。もちろんのことですが、最初はforce_destroy = falseに設定していて、バケット内オブジェクトが入っているので、強制変更はできません。

force_destroy - (Optional, Default: false) When deleting a bucket, this boolean option will delete all contained objects. If you try to delete a bucket that contains objects, Terraform will fail that run.

解決方法を二つ見つけました。

  1. バケット内のオブジェクトを消してから、再度terraform apply
  2. バケットを丸ごと削除

特別な理由がない限り、1の方が断然便利です。何らかの理由でこの開発用のバケットを丸ごと削除しないといけない、あるいは(筆者のように)コンソールからバケットを消しちゃったといった場合は追加の対策が必要です。

バケットが消された際の対策

まず、terraform planを実行すると

Error: Error acquiring the state lock
Error message: resource temporarily unavailable

TerraformとGCPのstateが噛み合わないので、ロックがかけられます。 terraform force-unlock {you-lock-id}を使って、ロックを解除できます。この操作はかなり危ないので、チームメンバーが直近作業したかを確認してからやったほうが良いでしょう。

再度terraform planを実行すると、Refreshing state...に永遠に止まっているように見えます。公式ドキュメントを調べると、環境変数を設定して再度planするとより詳細なログが見れます。

export TF_LOG=TRACE
terraform plan

https://www.terraform.io/cli/config/environment-variables

---[ RESPONSE ]--------------------------------------
HTTP/2.0 404 Not Found
Content-Length: 171
Alt-Svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000,h3-Q050=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43"
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Content-Type: application/json; charset=UTF-8
Date: Fri, 27 May 2022 01:12:48 GMT
Expires: Mon, 01 Jan 1990 00:00:00 GMT
Pragma: no-cache
Server: UploadServer
Vary: Origin
Vary: X-Origin
X-Guploader-Uploadid: xxxx

{
 "error": {
  "code": 404,
  "message": "The specified bucket does not exist.",
  "errors": [
   {
    "message": "The specified bucket does not exist.",
    "domain": "global",
    "reason": "notFound"
   }
  ]
 }
}

バケットがあるはずなのになかったぞというエラーメッセージが無限に出ています。理由としては、terraformのstateが古いままになっています。幸いなことにTerraformすでにterraform state rmという古いstateを消す機能を提供してくれています。最初に作ったバケットのstateを下記コマンドで削除します。

terraform state rm module.storage.google_storage_bucket.my_bucket

https://www.terraform.io/cli/commands/state/rm

もう一度terraform planを実行すると問題解決です。 Cloud Storage以外に、似たような事象が起きた際にも今回の経験を活用できそうですね。 それでは!