Google Workspaceのスプレッドシートを使うユーザーがCloud Run functionsの認証が必要なHTTPSエンドポイントを利用するために対応したこと
メディア事業本部サービス開発部でデジタル広告サービス(A-TANK)のデータ基盤整備を担当している落合です。
(本稿は朝日新聞社の2024年Qiitaアドベントカレンダーの投稿です)
今回は、Google WorkspaceのスプレッドシートからCloud Run functionsを利用する際に採用した構成について紹介します。
朝日新聞社では、デジタル広告事業におけるデータ基盤としてGoogle CloudのBigQueryを利用し、データソリューションの提供に活用しています。また事業部門のメンバーはGoogle Workspaceを利用し、ダッシュボード作成や企画提案、レポーティングといった活動において、Looker StudioやGoogle スプレッドシートをBigQueryと連携させて活用しています。Looker StudioではGoogleの各種サービス向けにコネクターが提供されており、BigQueryと連携することができます。また、Google スプレッドシートもBigQueryと連携できます。
今回、Google スプレッドシートで広告配信の運用レポートを作成するツールを開発しました。このツールはBigQueryと連携するだけではなく、ユーザーが実行したタイミングで外部のAPIにリアルタイムにアクセスしてレポートを生成します。将来的には、レポートの分析結果を外部APIへフィードバックする機能拡張も計画されており、ビジネスロジックが複雑になっていく可能性があります。また、外部のAPIへのアクセスは特定のユーザーのみ行えるようにする制限が求められました。
そこで、ビジネスロジックの組みやすさ、リアルタイム性やアクセス制限の要件から、通常行うBigQuery経由でのデータ連携ではなく、Cloud Run functionsの認証が必要なHTTPトリガーを利用して外部のAPIにアクセスする構成を考えてみました。Cloud Run functionsを特定のユーザーのみ実行できるようにし、外部のAPIへのアクセスで使用する認証情報はSecret Managerで安全に管理することで要件を満たします。Cloud Run functionsの従量課金は極めて小さい金額と想定されるため、コスト面にも優れた構成となりそうです。
下記のようなイメージです。
では、どのようにすれば実現できるかを見ていきましょう。
Google スプレッドシートは、メニュー項目の拡張機能からApps Scriptを使用することでGoogle Cloudと関連付けることができます。下記を実施することで、Google スプレッドシートを利用中のユーザーがOAuth認証を使用してGoogle Cloudの認証情報を取得することができます。
Apps Script
プロジェクトの設定を開き、「Google Cloud Platform(GCP)プロジェクト」の項目でGoogle Cloudのプロジェクト番号を設定する(Resource Managerのプロジェクト取得とOAuthクライアントの作成権限を持つユーザーで設定します)
プロジェクトの設定を開き、「「appsscript.json」マニフェスト ファイルをエディタで表示する」にチェックを入れ、認可スコープを編集する
Google Cloud
(事前準備として)今回の用途に応じたGoogle Cloudプロジェクトを用意し、関連するAPIを有効化する
Apps Scriptで関連付けたGoogle Cloudプロジェクトで、OAuth同意画面を作成する
これで、Apps Scriptでアクセストークン(OAuthトークン)を取得する準備が整いました。Apps Scriptで下記のコードを実行すると、Google Cloud APIにアクセスするためのアクセストークンが取得できました。
ScriptApp.getOAuthToken();
これで準備ができたように思われましたが、Cloud Run functionsの認証が必要なHTTPSエンドポイントへのアクセスにはアクセストークンではなく、IDトークンを利用する必要があります。Cloud Run functionsのHTTPSエンドポイントにアクセスするまで、もうひと手間必要です。
IDトークンの取得には、ユーザーアカウントのプリンシパルを使用するか、サービスアカウントを使用するかの2種類の方法が考えられます。セキュリティ面を考えると、最小権限の原則を守ることは重要です。今回のタスクの用途に特定されたサービスアカウントを用意し、ユーザーがサービスアカウントの権限を借用する形をとることにしました(ユーザーに広範な権限を付与する必要がなくなり、Apps Scriptのコードを誤って書き換えた場合でも想定外の動作を防ぐことができるといったメリットが考えられます)。また、将来的に外部APIへのアクセスをバッチ処理化するような要望が出た場合にも、サービスアカウントの方が柔軟に対応できそうです。
そこで、下記の対応を追加で行います。
Google Cloud
今回のタスク用に、サービスアカウントを作成する(特定のCloud Run functionsのみ実行できる権限を付与する)
外部APIへのアクセスが許可されたユーザーが、上記サービスアカウントの権限を借用できるようにする(ユーザーのプリンシパルに、上記サービスアカウントのIDトークンを作成できる権限を付与する)
これで、ユーザーがサービスアカウントのIDトークンを取得できるようになりました。下記のApps Scriptのサンプルコードでは、Service Account Credentials APIを実行してIDトークンを取得します。
response = UrlFetchApp.fetch(
"https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/<サービスアカウントのプリンシパル>:generateIdToken",
{
method: 'post',
headers: {
Authorization: "Bearer " + ScriptApp.getOAuthToken()
},
contentType: 'application/json',
payload: `{
"includeEmail": true,
"audience": "<Cloud Run functionsのHTTPSエンドポイントのURL>"
}`
}
);
これでCloud Run functionsのHTTPSエンドポイントにアクセスできます。下記のApps Scriptのサンプルコードでは、Service Account Credentials APIから取得したIDトークンを用いてエンドポイントにアクセスできます。
UrlFetchApp.fetch("<Cloud Run functionsのHTTPSエンドポイントのURL>",
{
'method' : 'post',
'headers': {"Authorization":"Bearer " + JSON.parse(response.getContentText()).token},
'muteHttpExceptions': true
}
);
Cloud Run functinonsのログを確認し、アクセスが成功することを確認できました。
備考
説明を単純化するため、実際の構成とは異なる点があります。
実際のIAM権限は、メンバーの入れ替わりによるメンテナンスを考慮し、Googleグループに対して付与しています
実際にはユーザーの所属するGoogle Workspaceとデータ基盤で利用する組織(Organization)が異なっており、もう少し複雑な構成を取っています
実際にはスプレッドシートとBigQueryの連携にGoogle ドライブも使用しています