terraform-provider-googleにgoogle_bigquery_datasetのバグ報告して、修正コードを取り込んでもらった話

仕事でGoogle Cloudのリソース管理にTerraformを使っていて、コードは一切変更していないのにterraform planで意図していない差分が出たり、terraform applyが通らなくなって手動でリソースの修正が必要になって困ったケースがありました。本家のterraform-provider-googleにバグ報告をして、修正コードを取り込んでもらったので、自分用の記録としてメモしておきます。

困った事象

注意: 修正コードを取り込んでもらったので、このエントリを読んでいるときの最新のterraform-provider-googleでは再現しない可能性があります。

問題を再現させるために、最小のterraformのコードを用意します。サービスアカウントとBigQueryのデータセット作成し、サービスアカウントにデータセットの権限を付与します。何の変哲もないコードですね。

resource "google_bigquery_dataset" "my_test_dataset" {
  project = "my-project"
  dataset_id = "my_test_dataset"
}

resource "google_service_account" "my_test_service_account" {
  project      = "my-project"
  account_id   = "my-test-service-account"
  display_name = "Service account for test"
}

resource "google_bigquery_dataset_iam_member" "my_test_service_account_has_bigquery_metadata_viewer_for_my_test_dataset" {
  project    = "my-project"
  dataset_id = google_bigquery_dataset.my_test_dataset.dataset_id
  role       = "roles/bigquery.metadataViewer"
  member     = "serviceAccount:my-test-service-account@my-project.iam.gserviceaccount.com"
}

今回は分かりやすさのためにサービスアカウントをTerraformで生成していますが、Issueにも起票しているようにGoogle Groupでも以下のことはバグが発生しますし、手動で作成したサービスアカウントやGoogle Groupでも同じことが起こると思います。my_test_service_accountの変数への参照でgoogle_bigquery_dataset_iam_memberを定義したほうがよいですが、そういったケースもあるため、ここでは直にサービスアカウントのアドレスを書いています。

リソース作成後のデータセットの権限付与の状況をbqコマンドで見てみると以下のようになっています。ここまでは普通です。

% bq show --format=prettyjson my-project:my_test_dataset | jq '.access'
[
  {
    "role": "roles/bigquery.metadataViewer",
    "userByEmail": "my-test-service-account@my-project.iam.gserviceaccount.com"
  },
  ...
]

問題はサービスアカウントを削除した場合です。Terraformのコードからサービスアカウントの箇所を消します(権限付与に関する行はそのままです)。すると、先ほどのbqコマンドの出力が以下のように変化します。

% bq show --format=prettyjson my-project:my_test_dataset | jq '.access'
[
  {
    "iamMember": "deleted:serviceAccount:my-test-service-account@my-project.iam.gserviceaccount.com?uid=123456789",
    "role": "roles/bigquery.metadataViewer"
  },
  ...
]

deleted:serviceAccount:...はアカウントが削除された際に出る表記ですが、注目ポイントはuserByEmailiamMemberに変わっている点です。この状態でデータセットのdescriptionを追加する変更を加えてみます。

resource "google_bigquery_dataset" "my_test_dataset" {
  project     = "my-project"
  description = "hoge fuga piyo!" # 追加した
  dataset_id  = "my_test_dataset"
}

この変更に対してterraform applyを実行すると、以下のエラーで失敗します。えっ、descriptionしか変えてないんだが...?となりました。

Error: Error updating Dataset "projects/my-project/datasets/my_test_dataset": googleapi: Error 400: An access entry must have exactly one of userByEmail, groupByEmail, domain, specialGroup defined, view, routine, or dataset., invalid

この状態になると割と困ったことになって、コードとしては修正するべき点はないが、terraform applyが通らない、という状態になります。やれる対応手段としてはBigQueryのコンソール上からdeleted:serviceAccount:my-test-service-account@my-project.iam.gserviceaccount.com?uid=123456789への権限付与を手動で削除する、というのがありますが、IaCでコード管理している身としてはこういったオペレーションは避けたいですよね...。

修正コード

ソースコードを注意深く読んでいると、google_bigquery_datasetaccessフィールドにはuserByEmailgroupByEmailは定義されているが、iamMemberは定義されていない!ということに気付きました。サービスアカウントやGoogle Groupなどが削除された & google_bigquery_datasetの設定を変えたい場合にのみ起きる問題なので、コーナーケースと言えばそうかもしれませんが、今後も手動オペレーションは嫌だなと思ったので、本家にコードを出して、無事に取り込んでもらいました。やったぜ!

所感

  • terraform-provider-googleはコード生成された結果のリポジトリで、大本を修正するにはmagic-modulesのリポジトリを修正しないといけないことに最初気付かなかった
    • ディレクトリ構造がよく分からなくて、生成元はどれや...と最初迷子になった
    • 設定ファイルがyamlになってて、rubyを使って(?)、terraform-provider-googleのGoのコードが生成されるって感じなのね
    • 自分のPRによって自動生成されたPRがマージされたのも見つけたので、どこかのバージョンからこのバグは起きなくなるはず
  • Contributionのガイドラインが丁寧に書いてあったので、そこを読みながら進めるとよい
  • CIと格闘していたら、メッセージ中に出るリンクが壊れていたので、これも直すPRを送った
  • ChatGPTのおかげでIssueやPull Requestを送る心理的障壁がぐっと下がったし、普段使ってるツールが壊れた場合でも「よっしゃ、contributionチャンスや!」と思って実際に動ける場面が増えてきたのは自分的に成長したなって自画自賛しておきます
    • 自分やチームメンバーが普段使いしているツールに、(たとえちょっとでも)自分のコードが寄与しているのは嬉しい
    • やっていきましょう!