Workload IdentityでApplication Default Credentials(ADC)できるらしいというのをみかけた。Github Actions上でdataform cliの実行(認証)ができるかどうかやってみた
色々制約はありつつもできそうなことがわかった
Google Cloud設定
github用のworkload Identityやら、利用する権限のサービスアカウントやらを作成する。リソースはterraformで作成した
terraform
terraform state保存用のbucketはあらかじめ一度作っておく必要がある
- ファイル
main.tf
locals {
state_bucket_name = "tf_state_object"
project_id = "projectId"
github_org_name = "orgName" # 個人アカウントの場合はユーザ名
github_repo_name = "repositoryName"
sa_roles = [
"roles/iam.serviceAccountTokenCreator",
"roles/secretmanager.secretAccessor",
"roles/dataform.serviceAgent",
"roles/bigquery.jobUser",
"roles/bigquery.dataOwner",
"roles/bigquery.dataEditor",
]
}
provider "google" {
project = local.project_id
region = "US"
}
terraform {
required_version = "1.9.0"
required_providers {
google = {
source = "hashicorp/google"
version = "~> 5.0"
}
}
backend "gcs" {
bucket = "tf_state_object"
prefix = "state"
}
}
# state backend用
resource "google_storage_bucket" "terraform_state_bucket" {
name = "tf_state_object"
location = "US"
force_destroy = true
storage_class = "STANDARD"
versioning {
enabled = true
}
uniform_bucket_level_access = true
}
# github actionsにてなりすますサービスアカウント
resource "google_service_account" "dataform_run" {
project = local.project_id
account_id = "dataform-run"
display_name = "dataform run"
}
resource "google_project_iam_member" "dataform_run" {
project = local.project_id
count = length(local.sa_roles)
role = element(local.sa_roles, count.index)
member = "serviceAccount:${google_service_account.dataform_run.email}"
}
# workload identity周りのリソース
resource "google_iam_workload_identity_pool" "github_actions" {
project = local.project_id
workload_identity_pool_id = "github-actions-pool"
}
resource "google_iam_workload_identity_pool_provider" "github_actions" {
project = local.project_id
workload_identity_pool_provider_id = "github-actions-provider"
workload_identity_pool_id = google_iam_workload_identity_pool.github_actions.workload_identity_pool_id
attribute_condition = "\"${local.github_org_name}/${local.github_repo_name}\" == assertion.repository"
oidc {
issuer_uri = "https://token.actions.githubusercontent.com"
}
attribute_mapping = {
"google.subject" = "assertion.sub"
"attribute.repository" = "assertion.repository"
}
}
# workload identityにサービスアカウントを接続
resource "google_service_account_iam_binding" "github_actions_iam_workload_identity_user" {
service_account_id = "projects/${local.project_id}/serviceAccounts/${google_service_account.dataform_run.email}"
role = "roles/iam.workloadIdentityUser"
members = [
"principalSet://iam.googleapis.com/${google_iam_workload_identity_pool.github_actions.name}/attribute.repository/${local.github_org_name}/${local.github_repo_name}",
"principal://iam.googleapis.com/${google_iam_workload_identity_pool.github_actions.name}/subject/repo:${local.github_org_name}/${local.github_repo_name}:ref:refs/heads/main"
]
}
github actionsにて使用するprovider名は以下のようになる。PROJECT_NUMBERはGoogle Cloudのプロジェクト番号
"projects/{PROJECT_NUMBER}/locations/global/workloadIdentityPools/github-actions-pool/providers/github-actions-provider"
- provider名取得コマンド
gcloud iam workload-identity-pools list --location="global"
---
name: projects/{PROJECT_NUMBER}/locations/global/workloadIdentityPools/github-actions-pool
state: ACTIVE
gcloud iam workload-identity-pools providers list --location="global" --workload-identity-pool=projects/{PROJECT_NUMBER}/locations/global/workloadIdentityPools/github-actions-pool --format=json | jq '.[].name'
"projects/{PROJECT_NUMBER}/locations/global/workloadIdentityPools/github-actions-pool/providers/github-actions-provider"
Google Cloud consoleから確認するとこんな感じ
気になったこと
- subjectとかrepositoryとは
github actions側で発行するトークンのことらしい。これを利用して、サービスアカウントに条件を付与しておくことで、どのリポジトリからでも実行できないようにするなどが実現できる
ref. トークンの種類と中身
- https://docs.github.com/ja/actions/deployment/security-hardening-your-deployments/using-openid-connect-with-reusable-workflows#how-the-token-works-with-reusable-workflows
Github Actionsでdataform cli実行
NG集が多いので先に 動作したものをのせる
CI compile & dry-run
実際にクエリが実行可能であるかを確認するgithub actionsを作成する
dataformは3.0.0からdry-runでも実際にクエリを実行するので、実際にテーブルや権限についても確認ができる
- gihtub actions workflow file
ci.yml
name: CI
on:
push:
paths:
- definitions/**
- includes/**
- .github/workflows/ci.yml
permissions:
contents: read
id-token: write
env:
SERVICE_ACCOUNT: "dataform-run@{projectId}.iam.gserviceaccount.com"
PROJECT_ID: "projectId"
WORKLOAD_IDENTITY_PROVIDER: "projects/{projectNumber}/locations/global/workloadIdentityPools/github-actions-pool/providers/github-actions-provider"
jobs:
dry-run:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Checkout code into workspace directory
uses: 'actions/checkout@v4'
- name: Auth google cloud
id: auth
uses: 'google-github-actions/auth@v2'
with:
project_id: ${{ env.PROJECT_ID }}
workload_identity_provider: ${{ env.WORKLOAD_IDENTITY_PROVIDER }}
service_account: ${{ env.SERVICE_ACCOUNT }}
access_token_lifetime: '300s' # optional, default: '3600s' (1 hour)
- name: With Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
- name: Install package
run: |
npm install -g @dataform/cli
- name: Check dataform version
run: dataform --version
- name: Run dataform compile
run: dataform compile
- name: Dry run
run: dataform run --dry-run
- 実行ログ抜粋。適当なviewを作成するactionを追加している。dataform cliの実行が通ることを確認した
...
Run dataform --version
3.0.0
...
Run dataform compile
Compiling...
Compiled 1 action(s).
1 dataset(s):
dataform.view [view]
...
Run dataform run --dry-run
Compiling...
Compiled successfully.
Dry running (no changes to the warehouse will be applied)...
Table dry run success: dataform.view [view]
記事ではCI用のworkflowファイルを示したが
これを単純に
dataform run —tags=test
注意点
- .df-credentials.jsonファイルが必要
デフォルトではcredentialファイルなどの情報が追加されうるので.gitignoreにもデフォルトで設定されているファイルであるが、こちらがリポジトリにないとエラーになる
ADC認証を行っているならばファイルの中身は以下のようになるので、秘匿情報は一応入らない
{
"projectId": "projectId",
"location": "US"
}
この場合はローカルでdataform cliを用いるときもADC認証で行う必要がある。試してないが、もしかしたらgithub actions実行時に.df-credentials.jsonファイルを生成するのでもいいかもしれない
.df-credentials.jsonがないときのエラー
Dataform encountered an error: Missing credentials JSON file; not found at path '/home/runner/work/dataform/.df-credentials.json'.
Error: Missing credentials JSON file; not found at path '/home/runner/work/dataform/.df-credentials.json'.
試行錯誤、エラー集
workflowファイルを見るとわかるが、dataform cliのインストール方法が特殊だったりする
- dataformのdockerfileが3.0.0からサポートされなくなった
そのためjob上でdataformをインストールしている
ref. https://github.com/dataform-co/dataform/issues/1771
- npm installでdataform cliを入れるとエラー
node_modulesがあるとエラーになった。そのためworkflow上ではグローバルインストールしている。もし必要なpackageが増えたらdockerfileを使うほうがいいかも
Dataform encountered an error: '/home/runner/work/dataform/node_modules' unexpected; remove it and try again
Error: '/home/runner/work/dataform/node_modules' unexpected; remove it and try again
所感
もう公式ドキュメントにページは残っていないが、元々はGPGを用いてサービスアカウントキーを暗号化して認証を行っていた(ref. https://blog.uni-3.app/dataform-init#secret-の登録)。今回の方法では、キーファイルを作成したり、GPGキーの複合パスワードを保持せずに済む。設定は多少大変だが、セキュアになったのでよしとする
dataformは3.0.0 からdry-runにて実際にクエリを実行するようになった。CI上でクエリが実行できる状態なのが心配であれば認証のstepは削除して、compile実行までにとどめておくのもいいかもしれない
dataform cliがgcloud cliに取り込まれれば、認証周りでもうちょっと楽できそうであるが
参考
Workload IdentityとGithub Actionsの連携周り
- https://qiita.com/leemunhui/items/d2a9f060341113b00c07
- https://zenn.dev/satohjohn/articles/1645be8e83eab6
- https://zenn.dev/cloud_ace/articles/7fe428ac4f25c8
- https://cloud.google.com/iam/docs/workload-identity-federation-with-deployment-pipelines?hl=ja#github-actions
ADC認証がいけるらしいののヒントになった
-
https://github.com/dataform-co/dataform/issues/1465 dockerは今後サポートしないらしい
-
https://github.com/dataform-co/dataform/issues/1771
-
https://cloud.google.com/dataform/docs/release-notes#July_18_2024
github actions
- https://github.com/google-github-actions/auth/blob/main/docs/EXAMPLES.md#workload-identity-federation-through-a-service-account