Dataform 環境を Terraform で構築する
- 公開日
- カテゴリ:Dataform
- タグ:Dataform,BigQuery,自分用メモ

Dataform の環境を Terraform で構築する。サービスアカウント、Secret Manager、Dataform リポジトリ、リリース設定、ワークフロー設定をコード化することで、環境の再現性・変更管理・レビューが可能になる。
contents
- 前提
- Terraform で管理するもの / しないもの
- GitHub 認証について
- ディレクトリ構成
- Step 1: Terraform の初期設定と API 有効化
- Step 2: Dataform module の作成
- Step 3: サービスアカウントと IAM
- Step 4: Secret Manager
- Step 5: Dataform リポジトリ
- Step 6: 開発ワークスペースで SQLX を作成
- Step 7: リリース設定・ワークフロー設定
前提
- GCP プロジェクトが作成済みであること
- Terraform がインストール済み
gcloudで認証済み
Terraform で管理するもの / しないもの
Dataform の運用には、Terraform と GitHub で役割を分担する。
Terraform が管理 GitHub + Dataform UI が管理
───────────────── ──────────────────────────
・API 有効化 ・SQLX ファイル(SQL 定義)
・Dataform リポジトリ ・開発ワークスペース
・リリース設定 ・変更履歴、PR レビュー
・ワークフロー設定
・サービスアカウント + IAM
・Secret Manager(GitHub 認証)
Terraform は「インフラの箱」を作り、中身(SQL コード)は GitHub で管理する。
GitHub 認証について
Dataform は GitHub リポジトリからコードを取得するため、認証が必要になる。認証方法は2つある。
| 項目 | PAT(Personal Access Token) | GitHub App |
|---|---|---|
| 紐づき | 個人アカウント | 組織 / リポジトリ |
| 退職リスク | あり(トークン無効化) | なし |
| 権限制御 | 粗い(repo 全体) | 細かい(リポジトリ単位、read/write) |
| 有効期限 | 期限切れリスク | 秘密鍵は期限なし |
| 適した用途 | 検証・個人開発 | 本番運用 |
本番運用では GitHub App を使うべきだが、本記事での検証では設定が手軽な PAT を使用する。
PAT の取得
GitHub → Settings → Developer settings → Personal access tokens → Tokens (classic) から PAT を作成する。
- スコープ:
repo(Full control of private repositories)
取得した PAT は後の Step で terraform.tfvars に設定する。
ディレクトリ構成
Terraform コードは Dataform の SQLX コードと同じリポジトリに配置する。
sample-bigquery-etl/
├── terraform/
│ ├── versions.tf # Terraform / プロバイダーのバージョン
│ ├── main.tf # プロバイダー設定 + module 呼び出し
│ ├── variables.tf # ルート変数定義
│ ├── terraform.tfvars # 変数値(.gitignore 対象)
│ ├── api.tf # API 有効化
│ ├── outputs.tf # module の出力を参照
│ │
│ ├── dataform/ # Dataform module
│ │ ├── variables.tf
│ │ ├── service_account.tf
│ │ ├── secret_manager.tf
│ │ ├── dataform.tf
│ │ └── outputs.tf
│ │
│ └── (他の module を追加可能)
│
├── definitions/ # SQLX ファイル(Dataform のコード)
│ ├── dl_sample/
│ ├── dwh_sample/
│ └── dm_sample/
├── workflow_settings.yaml # Dataform 設定
└── .gitignore
.gitignore
Terraform の秘密情報と state ファイルを Git 管理から除外する。
# Terraform
*.tfstate
*.tfstate.backup
.terraform/
.terraform.lock.hcl
terraform.tfvars
terraform.tfvars には GitHub PAT などの秘密情報を記載するため、Git には含めない。
Step 1: Terraform の初期設定と API 有効化
Terraform のプロバイダー設定と、Dataform に必要な API の有効化を行う。
versions.tf
terraform {
required_version = ">= 1.0"
required_providers {
google = {
source = "hashicorp/google"
version = "~> 6.0"
}
google-beta = {
source = "hashicorp/google-beta"
version = "~> 6.0"
}
}
}
Dataform のリソースは google-beta プロバイダーが必要なため、両方を定義する。
main.tf
provider "google" {
project = var.project_id
region = var.region
}
provider "google-beta" {
project = var.project_id
region = var.region
}
variables.tf
variable "project_id" {
description = "GCP プロジェクト ID"
type = string
}
variable "region" {
description = "デフォルトリージョン"
type = string
default = "asia-northeast1"
}
terraform.tfvars
project_id = "<YOUR-PROJECT-ID>"
api.tf
# Dataform
resource "google_project_service" "dataform" {
service = "dataform.googleapis.com"
disable_on_destroy = false # terraform destroy しても API は無効化されない
}
# Dataform(GitHub 認証用)
resource "google_project_service" "secret_manager" {
service = "secretmanager.googleapis.com"
disable_on_destroy = false
}
適用
cd terraform
terraform init
terraform plan
terraform apply
以下 API の有効化ができていること
Dataform API
https://console.cloud.google.com/apis/api/dataform.googleapis.com/metrics?hl=ja&project=<YOUR-PROJECT-ID>
Secret Manager API
https://console.cloud.google.com/apis/api/secretmanager.googleapis.com/metrics?hl=ja&project=<YOUR-PROJECT-ID>
Step 2: Dataform module の作成
ここから Dataform 固有のリソースを module として作成する。
module の呼び出し
ルートの main.tf に module 呼び出しを追加する。
# main.tf に追記
module "dataform" {
source = "./dataform"
project_id = var.project_id
project_number = var.project_number
region = var.region
github_pat = var.github_pat
}
module に渡す変数をルートの variables.tf と terraform.tfvars にも追加する。
# variables.tf に追記
variable "project_number" {
description = "GCP プロジェクト番号"
type = string
}
variable "github_pat" {
description = "GitHub Personal Access Token"
type = string
sensitive = true
}
github_pat には sensitive = true を設定し、terraform plan や terraform apply の出力にトークンの値が表示されないようにしている。
# terraform.tfvars に追記
project_number = "<YOUR-PROJECT-NUMBER>"
github_pat = "<YOUR-GITHUB-PAT>"
dataform/variables.tf
module が受け取る変数を定義する。
variable "project_id" {
description = "GCP プロジェクト ID"
type = string
}
variable "project_number" {
description = "GCP プロジェクト番号"
type = string
}
variable "region" {
description = "デフォルトリージョン"
type = string
}
variable "github_pat" {
description = "GitHub Personal Access Token"
type = string
sensitive = true
}
Step 3: サービスアカウントと IAM
Dataform はカスタムサービスアカウント(SA)を使って BigQuery を操作する。Google 管理の Dataform サービスエージェントがカスタム SA を「借用」して実行する仕組みになっている。
Dataform サービスエージェント(Google 管理、削除不可)
│
│ 借用(impersonate)
↓
カスタム SA(dataform-executor)
│
│ 実行
↓
BigQuery
dataform/service_account.tf
# Dataform ワークフロー実行用のカスタムサービスアカウント
resource "google_service_account" "dataform_executor" {
account_id = "dataform-executor"
display_name = "Dataform Workflow Executor"
description = "Dataform ワークフロー実行用"
}
# BigQuery ジョブユーザー(プロジェクトレベル)
resource "google_project_iam_member" "dataform_executor_bq_job_user" {
project = var.project_id
role = "roles/bigquery.jobUser"
member = "serviceAccount:${google_service_account.dataform_executor.email}"
}
# BigQuery データ編集者(dwh_sample)
resource "google_bigquery_dataset_iam_member" "dwh_editor" {
dataset_id = "dwh_sample"
role = "roles/bigquery.dataEditor"
member = "serviceAccount:${google_service_account.dataform_executor.email}"
}
# BigQuery データ編集者(dm_sample)
resource "google_bigquery_dataset_iam_member" "dm_editor" {
dataset_id = "dm_sample"
role = "roles/bigquery.dataEditor"
member = "serviceAccount:${google_service_account.dataform_executor.email}"
}
# BigQuery データ閲覧者(dl_sample)- ソースデータは読み取りのみ
resource "google_bigquery_dataset_iam_member" "dl_viewer" {
dataset_id = "dl_sample"
role = "roles/bigquery.dataViewer"
member = "serviceAccount:${google_service_account.dataform_executor.email}"
}
# Dataform サービスエージェントがカスタム SA を借用できるようにする
resource "google_service_account_iam_member" "dataform_agent_token_creator" {
service_account_id = google_service_account.dataform_executor.name
role = "roles/iam.serviceAccountTokenCreator"
member = "serviceAccount:service-${var.project_number}@gcp-sa-dataform.iam.gserviceaccount.com"
}
付与する権限の一覧
| 付与先 | ロール | 対象 |
|---|---|---|
| dataform-executor | BigQuery ジョブユーザー | プロジェクト |
| dataform-executor | BigQuery データ編集者 | dwh_sample, dm_sample |
| dataform-executor | BigQuery データ閲覧者 | dl_sample |
| Dataform サービスエージェント | トークン作成者 | dataform-executor |
dataform/outputs.tf
output "dataform_executor_email" {
value = google_service_account.dataform_executor.email
}
outputs.tf(ルート)
output "dataform_executor_email" {
value = module.dataform.dataform_executor_email
}
module の output をルートの output で参照する形にしている。
適用
terraform plan
terraform apply
Plan: 6 to add でサービスアカウントと各 IAM バインディングの作成が表示されれば OK。
適用後の確認:
- サービスアカウントが作成されていること
https://console.cloud.google.com/iam-admin/serviceaccounts?hl=ja&project=<YOUR-PROJECT-ID>dataform-executorが存在すること
- BigQuery のデータセットに権限が付与されていること
- dl_sample → データ閲覧者
- dwh_sample, dm_sample → データ編集者
Step 4: Secret Manager
事前に取得した GitHub PAT を Secret Manager に格納し、Dataform サービスエージェントに読み取り権限を付与する。
dataform/secret_manager.tf
# GitHub PAT を格納するシークレット
resource "google_secret_manager_secret" "github_pat" {
secret_id = "dataform-github-pat"
replication {
auto {}
}
}
# シークレットバージョン(PAT の値)
resource "google_secret_manager_secret_version" "github_pat" {
secret = google_secret_manager_secret.github_pat.id
secret_data = var.github_pat
}
# Dataform サービスエージェントにシークレットの読み取り権限を付与
resource "google_secret_manager_secret_iam_member" "dataform_agent_secret_accessor" {
secret_id = google_secret_manager_secret.github_pat.secret_id
role = "roles/secretmanager.secretAccessor"
member = "serviceAccount:service-${var.project_number}@gcp-sa-dataform.iam.gserviceaccount.com"
}
Dataform サービスエージェントに secretAccessor を付与するのは、Dataform が GitHub からコードを取得する際に、このシークレットを読み取る必要があるため。カスタム SA(dataform-executor)ではなくサービスエージェントに付与する点に注意。
適用
terraform plan
terraform apply
Plan: 3 to add でシークレット・バージョン・IAM の3リソースが表示されれば OK。
適用後の確認:
- Secret Manager にシークレットが作成されていること
https://console.cloud.google.com/security/secret-manager?hl=ja&project=<YOUR-PROJECT-ID>dataform-github-patが存在すること
- シークレットの「権限」タブで、Dataform サービスエージェントに
Secret Manager のシークレット アクセサーが付与されていること
Step 5: Dataform リポジトリ
Dataform リポジトリを作成し、GitHub リポジトリと連携する。
構築順序について
リリース設定とワークフロー設定は、main ブランチに workflow_settings.yaml と definitions/ が存在しないとコンパイルエラーになる。そのため、構築は以下の順序で行う。
Step 5: Dataform リポジトリを作成(terraform apply)
↓
Step 6: UI で開発ワークスペースを作成し、SQLX を開発
→ git push → PR → merge
→ main ブランチに workflow_settings.yaml と definitions/ が存在する状態にする
↓
Step 7: リリース設定・ワークフロー設定を作成(terraform apply)
変数の追加
GitHub リポジトリの情報を変数として追加する。
# variables.tf(ルート)に追記
variable "github_repo_owner" {
description = "GitHub リポジトリのオーナー"
type = string
}
variable "github_repo_name" {
description = "GitHub リポジトリ名"
type = string
}
# terraform.tfvars に追記
github_repo_owner = "<YOUR-GITHUB-USERNAME>"
github_repo_name = "<YOUR-REPO-NAME>"
# main.tf の module 呼び出しを以下に更新
module "dataform" {
source = "./dataform"
project_id = var.project_id
project_number = var.project_number
region = var.region
github_pat = var.github_pat
github_repo_owner = var.github_repo_owner
github_repo_name = var.github_repo_name
}
# dataform/variables.tf に追記
variable "github_repo_owner" {
description = "GitHub リポジトリのオーナー"
type = string
}
variable "github_repo_name" {
description = "GitHub リポジトリ名"
type = string
}
dataform/dataform.tf(リポジトリのみ)
この時点ではリポジトリの作成のみ行う。リリース設定・ワークフロー設定は Step 7 で追加する。
# Dataform リポジトリ(GitHub 連携)
resource "google_dataform_repository" "main" {
provider = google-beta
name = "sample-dataform"
region = var.region
git_remote_settings {
url = "https://github.com/${var.github_repo_owner}/${var.github_repo_name}.git"
default_branch = "main"
authentication_token_secret_version = google_secret_manager_secret_version.github_pat.name
}
workspace_compilation_overrides {
default_database = var.project_id
}
service_account = google_service_account.dataform_executor.email
}
provider = google-beta: Dataform リソースは google-beta プロバイダーが必要git_remote_settings: GitHub リポジトリと連携。Secret Manager に格納した PAT で認証workspace_compilation_overrides: 開発ワークスペースでのコンパイル時に使用するプロジェクトservice_account: ワークフロー実行時に使用するカスタム SA
適用
terraform plan
terraform apply
Plan: 1 to add で Dataform リポジトリの作成が表示されれば OK。
適用後の確認:
- BigQuery → Dataform にリポジトリが作成されていること
https://console.cloud.google.com/bigquery/dataform?hl=ja&project=<YOUR-PROJECT-ID>sample-dataformが存在すること
- リポジトリの設定で GitHub 連携が正しく設定されていること
Step 6: 開発ワークスペースで SQLX を作成
Dataform リポジトリが作成できたら、UI 上で開発ワークスペースを作成し、SQLX ファイルを開発する。
手順
- BigQuery → Dataform →
sample-dataformを開く - 開発ワークスペースを作成
- ワークスペースの初期化(
workflow_settings.yamlが自動生成される) definitions/配下に SQLX ファイルを作成- 実行して動作確認
- git push → PR → main ブランチに merge
main ブランチに workflow_settings.yaml と definitions/ が存在する状態になったら、Step 7 に進む。
Step 7: リリース設定・ワークフロー設定
main ブランチに SQLX コードが存在する状態で、リリース設定とワークフロー設定を Terraform で作成する。
dataform/dataform.tf に追記
# リリース設定(main ブランチを定期コンパイル)
resource "google_dataform_repository_release_config" "main" {
provider = google-beta
region = var.region
repository = google_dataform_repository.main.name
name = "main-release"
git_commitish = "main"
cron_schedule = "0 2 * * *" # 毎日 JST 2:00
time_zone = "Asia/Tokyo"
}
# ワークフロー設定(リリースを定期実行)
resource "google_dataform_repository_workflow_config" "daily" {
provider = google-beta
region = var.region
repository = google_dataform_repository.main.name
name = "daily-workflow"
release_config = google_dataform_repository_release_config.main.id
invocation_config {
transitive_dependencies_included = true
transitive_dependents_included = true
fully_refresh_incremental_tables_enabled = false
service_account = google_service_account.dataform_executor.email
}
cron_schedule = "0 3 * * *" # 毎日 JST 3:00 - リリースの1時間後
time_zone = "Asia/Tokyo"
}
- リリース設定: main ブランチの SQLX を毎日 JST 2:00 にコンパイルする。コンパイル結果が「リリース」として保存される
- ワークフロー設定: リリースを毎日 JST 3:00 に実行し、BigQuery テーブルを作成/更新する。リリースのコンパイルが完了してからワークフローが動くよう1時間の間隔を設けている
transitive_dependencies_included: 依存するテーブルも含めて実行transitive_dependents_included: 依存されているテーブルも含めて実行fully_refresh_incremental_tables_enabled: インクリメンタルテーブルの完全リフレッシュは無効
適用
terraform plan
terraform apply
Plan: 2 to add でリリース設定とワークフロー設定の作成が表示されれば OK。
適用後の確認:
- Dataform →
sample-dataform→ リリースとスケジュールにリリース設定が表示されていること - ワークフロー設定が表示されていること
初回リリースの手動実行
適用直後はリリースのコンパイル結果がまだ存在しないため、ワークフローを「今すぐ実行」するとエラーになる。
初回は手動でリリースを作成する必要がある。
- Dataform →
sample-dataform→ リリースとスケジュール main-releaseの「新しいリリースを作成」をクリック- コンパイルが成功したら、ワークフローの「今すぐ実行」が可能になる
以降はスケジュール(JST 2:00 コンパイル → JST 3:00 実行)で自動的に動作する。
まとめ
Terraform で作成したリソースの全体像。
terraform/
├── api.tf
│ ├── Dataform API
│ └── Secret Manager API
│
└── dataform/(module)
├── service_account.tf
│ ├── カスタム SA(dataform-executor)
│ ├── BigQuery ジョブユーザー(プロジェクト)
│ ├── BigQuery データ編集者(dwh_sample, dm_sample)
│ ├── BigQuery データ閲覧者(dl_sample)
│ └── トークン作成者(サービスエージェント → カスタム SA)
│
├── secret_manager.tf
│ ├── シークレット(dataform-github-pat)
│ ├── シークレットバージョン(PAT の値)
│ └── シークレット IAM(サービスエージェントに読み取り権限)
│
└── dataform.tf
├── Dataform リポジトリ(GitHub 連携)
├── リリース設定(毎日 JST 2:00 コンパイル)
└── ワークフロー設定(毎日 JST 3:00 実行)
Terraform は「インフラの箱」を構築し、中身の SQL コード(SQLX)は GitHub + Dataform UI で管理する。構築順序としては、リポジトリ作成 → SQLX 開発・merge → リリース/ワークフロー設定の順で行う必要がある点に注意。

