Ritolabo
  1. Home
  2. GoogleCloud
  3. Dataform
  4. Dataform 環境を Terraform で構築する

Dataform 環境を Terraform で構築する

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

Dataform の環境を Terraform で構築する。サービスアカウント、Secret Manager、Dataform リポジトリ、リリース設定、ワークフロー設定をコード化することで、環境の再現性・変更管理・レビューが可能になる。

contents

  1. 前提
  2. Terraform で管理するもの / しないもの
  3. GitHub 認証について
    1. PAT の取得
  4. ディレクトリ構成
    1. .gitignore
  5. Step 1: Terraform の初期設定と API 有効化
    1. versions.tf
    2. main.tf
    3. variables.tf
    4. terraform.tfvars
    5. api.tf
    6. 適用
  6. Step 2: Dataform module の作成
    1. module の呼び出し
    2. dataform/variables.tf
  7. Step 3: サービスアカウントと IAM
    1. dataform/service_account.tf
    2. 付与する権限の一覧
    3. dataform/outputs.tf
    4. outputs.tf(ルート)
    5. 適用
  8. Step 4: Secret Manager
    1. dataform/secret_manager.tf
    2. 適用
  9. Step 5: Dataform リポジトリ
    1. 構築順序について
    2. 変数の追加
    3. dataform/dataform.tf(リポジトリのみ)
    4. 適用
  10. Step 6: 開発ワークスペースで SQLX を作成
    1. 手順
  11. Step 7: リリース設定・ワークフロー設定
    1. dataform/dataform.tf に追記
    2. 適用
    3. 初回リリースの手動実行

前提

  • 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.tfterraform.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 planterraform 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-executorBigQuery ジョブユーザープロジェクト
dataform-executorBigQuery データ編集者dwh_sample, dm_sample
dataform-executorBigQuery データ閲覧者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。

適用後の確認:

  1. サービスアカウントが作成されていること
    • https://console.cloud.google.com/iam-admin/serviceaccounts?hl=ja&project=<YOUR-PROJECT-ID>
    • dataform-executor が存在すること
  2. 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。

適用後の確認:

  1. Secret Manager にシークレットが作成されていること
    • https://console.cloud.google.com/security/secret-manager?hl=ja&project=<YOUR-PROJECT-ID>
    • dataform-github-pat が存在すること
  2. シークレットの「権限」タブで、Dataform サービスエージェントに Secret Manager のシークレット アクセサー が付与されていること

Step 5: Dataform リポジトリ

Dataform リポジトリを作成し、GitHub リポジトリと連携する。

構築順序について

リリース設定とワークフロー設定は、main ブランチに workflow_settings.yamldefinitions/ が存在しないとコンパイルエラーになる。そのため、構築は以下の順序で行う。

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。

適用後の確認:

  1. BigQuery → Dataform にリポジトリが作成されていること
    • https://console.cloud.google.com/bigquery/dataform?hl=ja&project=<YOUR-PROJECT-ID>
    • sample-dataform が存在すること
  2. リポジトリの設定で GitHub 連携が正しく設定されていること

Step 6: 開発ワークスペースで SQLX を作成

Dataform リポジトリが作成できたら、UI 上で開発ワークスペースを作成し、SQLX ファイルを開発する。

手順

  1. BigQuery → Dataform → sample-dataform を開く
  2. 開発ワークスペースを作成
  3. ワークスペースの初期化(workflow_settings.yaml が自動生成される)
  4. definitions/ 配下に SQLX ファイルを作成
  5. 実行して動作確認
  6. git push → PR → main ブランチに merge

main ブランチに workflow_settings.yamldefinitions/ が存在する状態になったら、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。

適用後の確認:

  1. Dataform → sample-dataform → リリースとスケジュールにリリース設定が表示されていること
  2. ワークフロー設定が表示されていること

初回リリースの手動実行

適用直後はリリースのコンパイル結果がまだ存在しないため、ワークフローを「今すぐ実行」するとエラーになる。

初回は手動でリリースを作成する必要がある。

  1. Dataform → sample-dataform → リリースとスケジュール
  2. main-release の「新しいリリースを作成」をクリック
  3. コンパイルが成功したら、ワークフローの「今すぐ実行」が可能になる

以降はスケジュール(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 → リリース/ワークフロー設定の順で行う必要がある点に注意。

Author

rito

rito

  • Backend Engineer
  • Tokyo, Japan
  • PHP 5 技術者認定上級試験 認定者
  • 統計検定 3 級