GitHub Actions から OIDC を使って Google Workspace のドメイン内にのみ共有しているデータにアクセスする
Google Workspace の組織内限定で共有しているスプレッドシートなどのデータに定期的にアクセスして集計スクリプトを回したいときってありますよね。 GAS でやるっていう手もありますが、好きな言語を使えることや、コード管理・デプロイ周りの簡潔さを考えると GitHub Actions に軍配が上がると思います。
GitHub OIDC (OpenID Connect) を使うとGitHub Actions から、サービスアカウントを使ってGoogleのリソースにセキュアにアクセスすることができるようになります。リポジトリに認証情報を持たせる必要もなくなりますし、キーのローテーションも不要になります。
今回この仕組みを使って Google Workspace のドメイン内限定で共有しているドライブ内のファイルやスプレッドシートなどのデータにアクセスする方法をまとめます。
認証には Google が公式で提供している Actions を使用します。
Google Workspace の組織内限定のリソースにアクセスするためには、組織内の特定ユーザーになりすましてAPIにアクセスすることのできるアクセストークンを発行する必要があります。
google-github-actions/auth のセットアップ
README の Setting up Workload Identity Federation のセクション の通りに進めていきます。
上記ドキュメントの手順にない作業として、Google Workspace の特定ユーザーのなりすましアクセストークンを発行するためには以下のように roles/iam.serviceAccountTokenCreator
ロールを付与する必要があります。
gcloud iam service-accounts add-iam-policy-binding "my-service-account@${PROJECT_ID}.iam.gserviceaccount.com" \
--project="${PROJECT_ID}" \
--role="roles/iam.serviceAccountTokenCreator" \
--member="principalSet://iam.googleapis.com/${WORKLOAD_IDENTITY_POOL_ID}/attribute.repository/${REPO}"
Google Workspace の Domain-Wide Delegation を設定
この作業は Google Workspace の管理者でないと実行することができません。管理者でない場合は権限のある方にお願いしましょう。
Google Admin にアクセスし、セキュリティ>アクセスとデータ管理>APIの制御の項目を開きます。画面下の方にある「ドメイン全体の委任」を開いて、「新しく追加」をクリックします。
先ほど作ったサービスアカウントのクライアントIDを入れます。クライアントIDは Google Cloud Console でもみれますが、以下のコマンドでも確認できます。
gcloud iam service-accounts describe "my-service-account@${PROJECT_ID}.iam.gserviceaccount.com" \
--format "value(uniqueId)"
OAuth スコープには、アクセスしたいAPIに必要なスコープを追加します。
たとえばスプレッドシートへの読み取り権限であれば以下になります。詳細はこちら
https://www.googleapis.com/auth/spreadsheets.readonly
Google Workspace のユーザーになりすましたアクセストークンを発行する
Generating OAuth 2.0 access tokens の項目を参考に GitHub Actions の yaml を設定していきます。
-
workload_identity_provider
はドキュメント通りに作られたプロバイダーのリソース名をセットします。 -
service_account
はサービスアカウントのメールアドレスをセットします。 -
token_format
はaccess_token
に設定することでアクセストークンが発行されるようになります。 -
access_token_scopes
は API が必要なスコープをセットします。先ほど Google Admin で設定したものと同じスコープを入れるとよいでしょう。 -
access_token_subject
には、Google Workspace にいる実際のユーザーのメールアドレスを入れます。発行されたアクセストークンは、このユーザーからのアクセスであると Google の API は認識します。スプレッドシートであれば、このユーザーのアクセス権限があるシートを見ることができます。 -
access_token_lifetime
にはアクセストークンが有効な秒数をセットします。最大1時間(3600s
)です。
jobs:
sync:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
cache: npm
- run: npm ci
- id: auth
uses: google-github-actions/auth@v0
with:
workload_identity_provider: projects/00000000000/locations/global/workloadIdentityPools/xxxxxxx/providers/xxxxxxx
service_account: my-service-account@my-project-id.iam.gserviceaccount.com
token_format: access_token
access_token_scopes: https://www.googleapis.com/auth/spreadsheets.readonly
access_token_subject: user@example.com
access_token_lifetime: 1800s
- run: node index.mjs
env:
SHEET_ID: xxxxxxxxxx
SHEET_TITLE: xxxxxxxxxx
GOOGLE_ACCESS_TOKEN: ${{ steps.auth.outputs.access_token }}
その後のステップで、実際に API にアクセスするスクリプトを実行します。Actions の yaml 内で ${{ steps.auth.outputs.access_token }}
で生成されたアクセストークンを環境変数として渡しています。
今回は Node.js で google-spreadsheet
というスプレッドシート用のラッパーライブラリを使ってみました。
import { GoogleSpreadsheet } from "google-spreadsheet";
const SHEET_ID = process.env.SHEET_ID;
const SHEET_TITLE = process.env.SHEET_TITLE;
const GOOGLE_ACCESS_TOKEN = process.env.GOOGLE_ACCESS_TOKEN;
const doc = new GoogleSpreadsheet(SHEET_ID);
doc.useRawAccessToken(GOOGLE_ACCESS_TOKEN);
await doc.loadInfo();
const sheet = doc.sheetsByTitle[SHEET_TITLE];
const rows = await sheet.getRows();
console.info("> Spreadsheet loaded");
// Spreadsheet:
// | name | value |
// | ---- | ----- |
// | foo | bar |
rows.forEach((row) => {
if (row.rowIndex === 1) return;
console.info(`${row.name}: ${row.value}`); // "foo: bar"
});
これを GitHub Actions の schedule イベントで動作させれば、定期的にスプレッドシートのデータにアクセスして集計処理などを行うことができるようになります。
on:
schedule:
- cron: '0 15,3 * * *' # 毎日 JST 12:00, 0:00 に実行
Discussion