uni memo

Google Cloud Workload Identity 連携を利用してdataform cliをGithub Actions上で動かす

Google Cloud Workload Identity 連携を利用してdataform cliをGithub Actions上で動かす

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
などとすることで、任意のactionを実行できる。github actionsは実行時間で課金される料金体系なのでテーブルやviewの作成などワンショットですむクエリの実行をするとよさそう

注意点

  • .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
2024, Built with Gatsby. This site uses Google Analytics.