1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

BigQueryでServiceAccountに成り代わってクエリを実行する

Last updated at Posted at 2024-12-18

はじめに

この記事は🎄GMOペパボ エンジニア Advent Calendar 2024の18日目の記事です。
なんかBigQuery Advent Calendar 2024 の 12 日目の枠も空いてたので、そこにも入れました。

追記

CLOUDSDK_AUTH_IMPERSONATE_SERVICE_ACCOUNT という環境変数を使って Google Cloud 公式 CLI ツール bq で権限借用の状態でクエリ出来るとタレコミがあり、確認したところ使えましたので、それを使った方が良いです。

追記2

ドキュメントに確認しにいったら、どうやら公式では gcloud config set auth/impersonate_service_account SERVICE_ACCOUNT_NAME で認証して bq コマンドを使うという想定でした。
https://cloud.google.com/bigquery/docs/bq-command-line-tool?hl=ja#bq_service_account
ただ、いちいち gcloud config unset auth/impersonate_service_account をするのも面倒ですし、環境変数を使ってインラインで権限借用したほうが便利だと思います。

課題

BigQuery をバッチから扱う際、ServiceAccount に権限を付与しておいてバッチの実行時ユーザーにするのがよくあるパターンだと思います。ただ、ServiceAccount にちゃんと権限が付いたかどうかを確認するの、地味に面倒なんですよね。ServiceAccountの鍵を生成して手元に持ってきたりとか、あんまりしたくないじゃないですか。そこで、それを簡単にする方法をご紹介します。

Service Account Impersonation

サービス アカウントの権限借用という機能を使うと対象 ServiceAccount の権限を簡単にふるうことができるようになります。gcloud auth print-access-token --impersonate-service-account=hoge@example.iam.gserviceaccount.com みたいなコマンドで一時的に使える鍵が生成されます。これを使うには対象の ServiceAccount に対する roles/iam.serviceAccountTokenCreator が必要ですので、適宜つけておきましょう。ちゃんとやるなら PAM を使って一時的に権限昇格する仕組みを活用するのもいいでしょう。

実践

下記のようなスクリプトを bq_query_as みたいな名前で path が通ったところに置いておけば捗ります。gem install 無しで動くという縛りで書いているので、print-access-tokengcloud コマンドの呼び出しで実現しています。

#!/usr/bin/env ruby
require 'net/http'
require 'uri'
require 'open3'
require 'json'

PROJECT = ENV['GOOGLE_PROJECT']
raise("ENV['GOOGLE_PROJECT'] が必要") unless PROJECT
ENDPOINT = URI.parse("https://bigquery.googleapis.com/bigquery/v2/projects/#{PROJECT}/queries")

service_account, query = ARGV
raise "なんかヤバい: #{service_account}" unless URI::MailTo::EMAIL_REGEXP.match?(service_account)

command = "gcloud auth print-access-token --impersonate-service-account=#{service_account}"
_, access_token, _, _ = *Open3.popen3(command)
header = {
  'Authorization' => "Bearer #{access_token.read.chomp}",
  'Content-Type' => 'application/json'
}
body = {
  query: query,
  useLegacySql: false
}

http = Net::HTTP.new(ENDPOINT.host, ENDPOINT.port)
http.use_ssl = true
request = Net::HTTP::Post.new(ENDPOINT.request_uri, header)
request.body = body.to_json

response = http.request(request)
puts response.body

実行の仕方の例↓。SELECT INSERT UPDATE DELETE など、確認したい権限に応じた SQL を投げてやれば、権限エラーが出ないかどうかだけでも確認ができます。

$ bq_query_as batch-user@example.iam.gserviceaccount.com "SELECT 1 AS n"

このように impersonate で実行すると、ログには誰が誰の権限を使って実行したかが出てくるので、後々、必要に応じて監査することもできます。

"authenticationInfo": {
  "principalEmail": "batch-user@example.iam.gserviceaccount.com"
  "serviceAccountDelegationInfo": [
    {
      "firstPartyPrincipal": {
        "principalEmail": "yancya@nantyara.com"
      }
    }
  ]
}

本当は

出来れば、↑みたいなお手製のツールではなく、bq query --impersonate-service-account=... とか gcloud bq query --impersonate-service-account=... が存在してほしいですね。今後、もしそれらが出てきたらそれを使います。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?