はじめに
この記事は🎄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-token
は gcloud
コマンドの呼び出しで実現しています。
#!/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=...
が存在してほしいですね。今後、もしそれらが出てきたらそれを使います。