❄️

本番稼働中の大規模Snowflake基盤をTerraform管理へ移行した話

に公開

この記事はSnowflake Advent Calendar 2025の12日目の記事です。

データマネジメントチームでデータエンジニアをしている是枝です。

皆さん、データ基盤のコード化に取り組まれていますか?一説によると基盤のInfrastructure as Code (IaC化)を行っている企業は、3割ほどと聞いたことがあります。
入社してから4ヶ月ほどが経過し、一番最初に取り組み始めたSnowflakeのIaC化のためのTerraform導入が形になり始めてきたため、記事にしようと思います。

Terraform移行の背景・課題

弊社では、データウェアハウス製品としてSnowflakeを採用しています。
私が入社しSnowflakeデータ基盤を見てまず最初に課題に感じたことは、SnowflakeのリソースがIaC化されていないことでした。それに伴い当然CI/CDも用意されておりませんでした。そのため、以下のような課題を抱えておりました。

課題 説明
運用管理の属人化 特定の担当者しか知らないリソース・設定が存在し、全体像の把握が困難。チームでのリソース管理ができない。
本番に変更を加えるリスク ユーザーが利用する本番環境に直でリソース変更するため、ユーザー影響がある変更かどうかぶっつけ本番になる。
リソースのレビューができない GUIでの操作なので、他のメンバーのレビューを通すことができない。設定ミスや誤操作が発生していても気付けない。
環境の再現性がない GUIでの操作につき、同じ構成を正確に再現することが困難。環境を再現する際は手作業が必要となり、人的コストやミスのリスクが増大


IaC導入前のリソース管理方法

多くの企業が上記の画像のように社内のデータが集約された本番環境とちょっと技術検証するような開発環境の2アカウントで運用をされていると想像されます。別に悪いわけではないですが、このような構成だと「運用管理の属人化」、「ユーザー影響がある変更かどうかぶっつけ本番になる」、「リソース作成・変更・削除のレビューができない」、「環境の再現性がない」といった課題が出てくるはずです。
私自身、前職でSnowflakeのリソース管理をIaC化ツールの一種であるTerraformで運用しており、基盤のIaC化の重要性を十分認識していました。Terraformを一度使ったユーザーはわかると思いますが、Terraformを使うとTerraformがない運用が考えられなくなります。そのため入社して一番最初に取り組むプロジェクトとしてSnowflakeにTerraformを導入することを決断しました。

途中からTerraformを導入する困難

Terraformは途中から導入するのはなかなか大変な作業です。UIから作成された既存のリソースをTerraformで定義し直し、stateファイルにimportしていく作業が必要になります。これが大変な作業で、現在のリソースの設定値を完全にTerraformに反映させなければ、Terraformのstateとplan結果に差分(つまり、不整合が起こる)が出てしまいます。リソースが大量にあるエンタープライズな基盤ほどこの作業は大変になると想像に難くないでしょう。できれば基盤を導入したタイミングでTerraform管理をするのがベストです。

さらに ビジネス成果としての困難もつきまといます。 基盤のIaC化だけで売上や利益が生まれる訳ではないので、導入の必要性をしっかり説明できる必要があります。また絶対ないと運用できないわけではなく、多くの方がUIポチポチで事足りている状況であるため、価値ある新機能開発が優先されどうしても優先度が下がりがちです。Terraformは運用者目線でのメリットは大きいですが、組織全体での合意形成には丁寧な説明とメリットの整理が不可欠です。

弊社のSnowflake基盤は既に活用がかなり進んでいました。何千近くのリソースがすでに手動作成済みで、途中からTerraformを導入する大きな障壁となっていました。そのため、まずは工数確保を進めるために、Terraformを導入するビジネスメリット(Why/What)の整理から、プロジェクトの理解を得ることにしていきました。

我々の課題に対してTerraformがどう解決してくれるのか

前述した「運用管理の属人化」「ユーザー影響がある変更かどうかぶっつけ本番になる」「リソース作成・変更・削除のレビューができない」「環境の再現性がない」といった課題に対して、Terraformがどのように解決してくれるのかを整理しました。

課題 Terraformによる解決策
運用管理の属人化 コード化により誰でも同じ品質で運用可能。特定の担当者に依存しない運用体制を構築。
ぶっつけ本番での変更 dev/stg/prodの段階的な検証環境をコードベースで構築し、本番前に影響を確認可能。
レビューができない Pull Requestを通じた変更レビューを必須化。チーム全体で変更内容をチェック。
環境の再現性がない 同一の構成をコードから即座に再構築可能。設定漏れや手作業による差分を排除。
野良リソースの発生 システムユーザーのみが強い権限を保持し、人間ユーザーは適切な権限に制限。
リソースの棚卸しトイル Terraformのstateファイルにより、全リソースの現状把握と管理が自動化。

例えば本番と開発環境がある会社がTerraformとCI/CDを導入するだけで、以下のような簡易的構成を実現でき、Terraformのメリットを十分享受することができます。

TerraformとCI/CDがある世界

これにより、人間ユーザーがACCOUNTADMIN、SYSADMINといった権限が強いシステムロールを持つ必要がなくなり、システムユーザーのみがこれらシステムロールを持って基盤に変更を加えられる状況を作ることができます。いつの間にか管理者の知らないところで生まれる野良リソースの発生を防ぐことができますし、定期的に発生しがちな 「リソースの棚卸し」というトイルを解消する のに役立ちます。

一方で、Terraform導入時に想定される課題も整理しました。

導入時の課題 対策・考慮点
リソース変更の即時性低下 コード修正→レビュー→デプロイのプロセスで時間がかかるが、品質の属人化を防ぐ重要なトレードオフとして受け入れ。
学習コスト GitHub CopilotやChatGPT等のAIツールがHCL生成をサポートし、従来より大幅にアクセスしやすく。
既存リソースのimport工数 AIによるimport文の自動生成やリソース定義提案により、手作業コストを大幅削減。

弊社でも1つ目の「リソース変更の即時性が低下して運用負荷が増加」が特に懸念点となりました。ユーザーからのデータ追加依頼や権限の調整などといった問い合わせ対応に素早く対応できなくなりユーザーに不便を強いることになってしまう、といった課題です。
一方で、素早く対応するという点は聞こえがいいですが、品質が安定しなくなるというリスクをはらんでいます。基盤開発の質が管理者個人に委ねられてしまうため、レビューや監査プロセスをすっ飛ばされ、基盤のサービスレベルが低くなる方向にあります。このような課題を 「品質の属人化」 と個人的に呼んでいます。優秀な基盤管理者が一人いれば質は一定担保されるかもしれませんが、その人が退職してしまった場合どうなるでしょう?別の品質観点を持った人が今度は運用することになり品質が一定になりません。そのため適切なプロセスに品質管理を寄せる必要があり、TerraformとCI/CD管理は最適なのです。

学習コスト、ツール依存リスク、既存リソースのimportに開発工数がかかるという観点は、昨今、AI開発が浸透してきたため、コード化することのメリットの説明はしやすくなっているかもしれません。

特にTerraformの学習コストについては、GitHub CopilotやChatGPT等のAIツールがHCL(HashiCorp Configuration Language)のコード生成をサポートしているため、従来よりもアクセスしやすくなっています。また、既存リソースのimport作業についても、AIがimport文の自動生成やリソース定義の提案を行ってくれるため、手作業での調査・記述コストが大幅に削減されています。

実際に弊社でも、Terraform初学者がAIアシスタンスを活用することで、従来の1/10の時間でTerraformコードの記述ができるようになりました。これは「コード化による生産性向上」という観点で、ビジネスサイドにも理解を得やすい材料となっています。

さらに、ナレッジの属人化解消という点でも、AIの存在が大きな意味を持ちます。従来は特定の担当者の知見に依存していたインフラ設定が、コード化とAIサポートにより「誰でもメンテナンス可能」な状態になります。これは組織のリスク軽減とスケーラビリティの向上に直結する価値提案となります。

上記のようなメリット・デメリットを整理した結果、弊社の環境において導入したほうがメリットが大きいという判断になりました。

観点整理

Terraformを基盤に導入を進めるにあたり、要件・仕様・設計と整理を進めました。Terraformを導入すると言っても、今回のスコープはCI/CDの設計やリポジトリの整備・環境分割方針も含まれたため、多様な観点からの整理となりました。ざっと以下の観点について整理を進めて、必要あれば各関係者を呼んで意思決定のためのMTGを開催していきました。

要件整理

検討項目 検討内容 決定事項・候補
管理対象リソース コード化対象の決定 対象: Warehouse / Database / Schema / Role / Policy / Service User
対象外: Table / View(dbt管理)/ User(一部のみ)
import優先順位 既存リソースの取り込み順序 1. アカウントレベル
2. データベースレベル
3. スキーマレベル
CI/CD実行環境 GitHub ActionsからSnowflakeへの接続方式 • AWS ECS/ECR
• AWS EC2
• Self-hosted runner
Snowflake-managed Network Rules
Git運用フロー ブランチ戦略とリリースフロー GitFlow vs GitHub Flow
将来のリリースフロー前提定義
環境分割方針 dev/stg/prod構成 • ステージング環境要否
• dev環境でのUI操作可否
• Sandbox環境の必要性
• 将来のアカウント分離
ローカル開発 開発者の作業環境 • Docker環境からのapply
• 認証情報管理(.env, AWS SSM, Vault)
State管理 Terraformの状態管理 • S3 + S3 Native Lock
• 環境別・リソース別分割単位
• 命名規則
責務分離 ツール間の役割分担 dbt: Table/View(変換処理)
Terraform: RBAC/構造管理
リポジトリ構成 コード管理方式 Terraform専用リポジトリの要否

仕様整理

仕様項目 検討内容 具体的な選択肢・決定事項
CI/CD構成 実行環境の具体案 GHA → AWS(ECS/ECR/EC2 or Self-hosted runner)→ Snowflake
コンテナ型 vs EC2常駐の比較検討
ネットワーク要件 接続方式の決定 • 固定IPの必要性
• PrivateLink要否
• NAT/Public IP可否
ワークフロー CI/CDパイプライン工程 lint → fmt → validate → plan → apply
必須ツール 品質管理ツール tfcmt: PR コメント自動化
tflint + reviewdog: コード品質チェック
レビューフロー コードレビュー運用 • GitHub Flow採用理由
• リリースフロー(タグ運用、環境昇格)
• バージョン戦略
IaCツール選定 最終技術選択 • Terraform + Terragrunt採用可否
• CREATE OR ALTER対応
• Pulumi/CDKTF/titan不採用理由
ローカル実行 開発者環境仕様 • Dockerコンテナ実行
• 認証情報管理(key pair/env/AWS profile)
• 固定IP接続の利点
Stateファイル 状態管理設計 • S3バケット設計
• 分離方針(役割 or リソース単位)
• S3 Native Lock命名ルール
Provider管理 バージョン戦略 Snowflake Provider固定方針(~> 互換性ポリシー)
AWS環境 インフラ構成 • dev/stg/prod 分離有無
ドキュメント 意思決定記録 ADR(Architecture Decision Record)作成

設計

設計領域 設計項目 具体的な設計内容
アーキテクチャ システム構成図 パターン1: GHA → AWS ECS → NAT/EIP → Snowflake
パターン2: Self-hosted runner → Snowflake
コード構成 ディレクトリ/モジュール modules/配下に以下を配置:
• database
• schema
• warehouse
• role
• grant
Provider/Backend Terraform設定 Provider: Snowflake Provider バージョン固定
Backend: S3(key/bucket/encrypt設定)
State分離: 環境 × リソース単位
認証・権限 セキュリティ設計 • Snowflakeサービスユーザー定義
• Secrets管理(GitHub Actions Secret / AWS Secrets Manager)
• キーペア認証運用
• Apply実行ロール(システム or カスタム)
ネットワーク 接続制御 Network Policy設定(EIP/NAT/PrivateLink許可IP)
移行戦略 import設計 取り込み順序:
1. RBAC(Role/Grant)
2. Tag/Policy
3. Schema
4. Warehouse
5. Database

運用方針: import後はTerraformのみで変更管理
CI/CD実装 パイプライン詳細 • tfcmt、reviewdog、tflint組み込み
• PRコメントでplan結果表示
• 環境昇格フロー設計

このように整理が必要なポイントをざっと書き出してみることで様々な観点から検討しなければならないことが分かります。Terraform導入においてこの整理が一番時間がかかりました。

工夫ポイント

ここではTerraform導入にあたり、重要な意思決定をピックアップして紹介したいと思います。今回はTerraformとは何か?といった初歩的な内容は扱わず、ある程度知っている前提で記載していきます。

管理するSnowflakeリソース範囲の決定

Terraformでどこまでのリソースを管理するかという判断は、プロジェクト成功の鍵を握る重要な意思決定です。すべてのリソースをコード化することが理想ではありますが、現実的には他のツールとの責務分担や、チームの運用体制を考慮する必要があります。

以下のようなスプシを一枚作り、それぞれどのリソースをどの優先度でTerraform化するかを整理していきました。

SnowflakeのTerraform管理すべきリソースと優先度を決めるスプシ

弊社では、インフラストラクチャとRBACに関わるリソースをTerraformで管理するという方針を採用しました。具体的には、Warehouse、Database、Schema、Account Role、Database Role、Grant、Policyといった基盤の骨格となるリソースと、CI/CDから実行するためのService Userを対象としています。これらのリソースは変更頻度が比較的低く、かつ慎重な管理が求められるため、コードレビューとCI/CDプロセスを経由することで品質を担保できます。

管理ツール 対象リソース 理由
Terraform Warehouse, Database, Schema, Account Role, Database Role, Grant, Policy, Service User など 変更頻度が低く、慎重な管理が必要。インフラの骨格となるリソース。
dbt Table, View 分析要件に応じて頻繁に変更。SQLベースでデータエンジニアに馴染みやすい。
管理対象外 User(一部除く) 人事システムやIDaaSとの連携で管理。Terraformでのハードコードは非現実的。

一方で、TableやViewについてはdbtによる管理を前提としており、Terraformの管理対象外としました。これらのデータモデルは分析要件に応じて頻繁に変更されるため、専用のデータ変換ツールであるdbtで管理する方が効率的です。dbtはSQLベースでTableやViewの定義を管理でき、データエンジニアにとって馴染みやすいツールです。このように、ツールごとの得意領域に応じて責務を分離することで、運用負荷を最小化しています。

またUserについても基本的にTerraform管理対象外としました。多くのユーザーは人事システムやIDaaS(Identity as a Service)との連携で管理されることが一般的であり、Terraformでユーザーアカウントをハードコードするのは現実的ではありません。ただし、CI/CDパイプラインから実行するService Userについては例外で、これらはTerraformで明示的に管理し、適切な権限設定とキーペア認証を構成しています。

既存リソースのimport優先順位についても戦略的に検討しました。Snowflakeのリソースには明確な階層構造が存在します。最上位のアカウントレベル(Warehouse、Account Role、Policyなど)から始まり、データベースレベル(Database、Database Role)、そしてスキーマレベル(Schema、Grantなど)へと続きます。import作業では、この依存関係を考慮して上位層から順番に取り込んでいくことで、リソース間の参照エラーを回避できます。

このように管理範囲を明確に定義することで、「何をTerraformで管理し、何を管理しないのか」がチーム全体で共有され、運用時の混乱を防ぐことができます。また、将来的に管理対象を拡大する際にも、この方針を基準として判断できるようになります。

CI/CD・環境分割(dev/stg/prod)の方針決定

最終的なCI/CDやSnowflake環境は以下のようになりました。

各環境の役割は以下の通りです。

環境 目的・意味 備考
開発環境 開発者が日常的に検証・試行を行うための環境。 ・UIからの変更・追加可能
・ローカルからTerraformの planapply を自由に実行できる
ステージング環境 本番環境と同等の構成を持つ検証用環境。本番リリース前の動作確認を行う。 ・developブランチの状態が反映される
・UIからの変更は禁止
・ローカルからTerraformの planapply を自由に実行できる
本番環境 本番データが存在する実運用環境。 ・mainブランチの状態が反映される
・GHAから本番リリースタグが付与されたときのみ apply される
・UIからの変更は禁止
・開発者はローカルから planapply もできない

基本的に、本番環境のSnowflakeには、GitHub Actionsからしかリソース変更ができないようになっています。そのため、人間ユーザーはACCOUNTADMIN、SYSADMINを持たない設計になっています。
しかしながら、本番の状態がどうなっているのか確認することも大事です。そのためステージング環境を「本番環境と同等の構成を持つ検証用環境」と位置づけて運用することにしました。本番環境との違いは、本番データが存在するかどうかになります。
開発者が日常的に検証・試行を行うための環境として開発環境を用意しています。人間ユーザーにも強めの権限を渡して、技術的な検証を行ったりするサンドボックス的な役割も含まれています。予告無しで定期的にリソースをお掃除することを前提にユーザーに技術検証してもらいます。
これによってガバナンスを効かせつつ、ユーザーに利便性を持たせられる設計が実現できます。

Terragrunt導入の決定

Terraformは確かに強力なツールですが、コードの重複といった課題に直面します。例えば各モジュールやルートモジュールで同じProvider設定をDRYに記述できず保守性が低下します。

# Provider 設定の一例。各モジュールで設定する必要がある。
terraform {
  required_providers {
    Snowflake = {
      source  = "Snowflake-Labs/Snowflake"
      version = "~> 0.90"
    }
  }
  backend "s3" {
    bucket = "terraform-state-dev"
    key    = "Snowflake/terraform.tfstate"
    region = "us-east-1"
  }
}

provider "Snowflake" {
  account = "dev-account"
  region  = "us-east-1"
}

そのため、弊社ではTerragruntの導入を決定しました。
Terragruntの最大のメリットはProviderやBackendなどの設定のDRY化だと思っています。Providerを例に記述しますが、通常のTerraformでは、各モジュールで同じProvider設定を重複して記述する必要がありますが、Terragruntのfind_in_parent_folders機能により、親ディレクトリで一元管理できるようになります。

Before(Terraformのみ):各モジュールで重複

# database/main.tf
terraform {
  required_providers {
    snowflake = {
      source  = "snowflakedb/snowflake"
      version = "~> 2.9"
    }
  }
}

provider "snowflake" {
  account = "your-account"
  user    = "SYSTEM_TERRAFORM"
  role    = "SYSADMIN"
}

# schema/main.tf  ← 同じ設定を再度記述
terraform {
  required_providers {
    snowflake = {
      source  = "snowflakedb/snowflake"
      version = "~> 2.9"
    }
  }
}

provider "snowflake" {
  account = "your-account"
  user    = "SYSTEM_TERRAFORM"
  role    = "SYSADMIN"
}

After(Terragrunt):一箇所で管理

# root.hcl(ルート設定)- Provider設定を一元管理
generate "provider" {
  path      = "provider.tf"
  if_exists = "overwrite_terragrunt"
  contents  = <<EOF
terraform {
  required_providers {
    snowflake = {
      source  = "snowflakedb/snowflake"
      version = "~> 2.9"
    }
  }
}

provider "snowflake" {
  account       = var.account_name
  user          = "SYSTEM_TERRAFORM"
  role          = var.role
  authenticator = "SNOWFLAKE_JWT"
  private_key_path = var.private_key_path
}
EOF
}

# database/terragrunt.hcl - Provider設定は自動継承
include {
  path = find_in_parent_folders("root.hcl")  # 親の設定を継承
}

terraform {
  source = "../../modules/database"
}

# schema/terragrunt.hcl - Provider設定は自動継承
include {
  path = find_in_parent_folders("root.hcl")  # 親の設定を継承
}

terraform {
  source = "../../modules/schema"
}

これにより、Provider設定の変更(バージョンアップ、認証情報の変更など)が発生した際も、ルート設定ファイルの1箇所を修正するだけで全モジュールに反映されます。

# terragrunt.hcl (ルート) - 一箇所で設定を共通化
remote_state {
  backend = "s3"
  config = {
    bucket = "terraform-state-bucket"
    key    = "${path_relative_to_include()}/terraform.tfstate"  # 自動生成
    region = "us-east-1"
    encrypt = true
  }
}

# schema/terragrunt.hcl - 依存関係をシンプルに記述
dependency "database" {
  config_path = "../database"  # 相対パスで指定
}

inputs = {
  database_name = dependency.database.outputs.database_name  # 自動解決
  schema_name   = "PUBLIC"
}

Terragrunt導入の際、シンプルフォーム株式会社の山岸さんの発信を参考にし、構成で悩むたびに何度も見返させていただきました。(この記事がなかったらTerragruntの導入を完遂できなかったであろうと思うくらい何度も見返させていただいたため本当に感謝しております。)
https://zenn.dev/dataheroes/articles/20240701-multi-account-snowflake-with-terragrunt
https://speakerdeck.com/roki18d/dataopsnight-number-8-terragruntwoyong-itasukerahurunasnowflakeinhuraguan-li

ディレクトリ構造

色々と試行錯誤してみましたがTerragruntを活用した最終的なディレクトリ構造は以下に落ち着きました。

./
├── root.hcl                       # ルート設定(全環境共通)
├── common/                        # 全環境共通のリソース定義
│   ├── databases.yaml
│   ├── schemas.yaml
│   ├── warehouses.yaml
│   ├── account_roles.yaml
│   └── ...
├── envs/
│   ├── dev/
│   │   ├── env.hcl               # 環境固有の変数
│   │   ├── common/               # dev環境固有のリソース定義
│   │   │   ├── databases.yaml
│   │   │   └── ...
│   │   ├── database/
│   │   │   └── terragrunt.hcl
│   │   ├── schema/
│   │   │   └── terragrunt.hcl
│   │   ├── warehouse/
│   │   │   └── terragrunt.hcl
│   │   ├── account_role/
│   │   │   └── terragrunt.hcl
│   │   └── ...
│   ├── stg/ (同じ構造)
│   └── prod/ (同じ構造)
└── modules/
    ├── database/
    │   ├── main.tf
    │   ├── variables.tf
    │   └── outputs.tf
    ├── schema/
    ├── warehouse/
    ├── account_role/
    └── ...

特に以下のような観点を拘って作ってみました。

リソースごとにmoduleを作ることでstateを分割

Terraformでエンタープライズレベルの基盤を管理する際の課題の一つが、stateファイルの肥大化と依存関係の複雑化です。単一stateファイルは管理が簡便ですが、全リソースのplan/apply実行時間が長くなる(数十分~数時間)ことや、複数人での同時作業時のstate lock競合が起こりやすくなるという課題があります。

弊社では数千のリソースを管理する必要があったため、リソース種別ごとにmoduleとstateファイルを分割する戦略を採用しました。各リソース種別で独立したstateファイルを持つことで、実行時間の大幅短縮を実現できます。

# 単一リソース種別のみ実行(数秒~数分)
cd envs/dev/database
terragrunt plan

# 必要に応じて関連リソースのみ実行
terragrunt run-all plan --terragrunt-include-dir database --terragrunt-include-dir schema

またチーム開発での競合もある程度回避できます。例えば、Database担当者がdatabase/配下のみ変更、権限担当者はaccount_role/grant/配下のみ変更すれば、それぞれ独立したstate lockで競合が起こりえません。この構成により、Grantリソースは必要な依存リソースが確実に作成された後に実行され、適切な順序で基盤構築が行われます。

ちなみにステート管理ではS3 Native Lockを初めて使用してみました。backend設定でuse_lockfile = trueと記述するだけでDynamoDBなしでステートロックが実現できてとても便利です。

remote_state {
  backend = "s3"
  config = {
    bucket         = "terraform-state-bucket"
    key            = "${path_relative_to_include()}/terraform.tfstate"
    region         = "us-east-1"
    use_lockfile   = true              # S3 Native Lockを使用
  }
}

YAMLベースのリソース定義による運用効率化 ( common/.yaml )

Terraformを導入する際、メンバーでSnowflakeのTerraformについて運用経験がある方が私しかいませんでした。Terraformを導入する以上は最低限は運用ができなければなりません。そのため、YAMLベースでリソースを記述することにし、学習コストを下げました。

# common/databases.yaml - 全環境共通のDatabase定義
databases:
  analytics:
    name: ANALYTICS
    is_transient: false

  application:
    name: APPLICATION
    is_transient: false

  data_management:
    name: DATA_MANAGEMENT
    is_transient: false

  staging:
    name: STAGING
    is_transient: false

envs/dev/common/.yaml 配下に環境固有の設定があれば書きます。

# envs/dev/common/databases.yaml - dev環境固有のDatabase定義
databases:
  sandbox:
    name: SANDBOX
    is_transient: true
    comment: "Development sandbox database"

これによって、運用者は最低限YAMLでリソース記述さえできれば、最悪Terraformのことがわからなくても運用を開始することができます。

全環境共通commonと環境固有commonは以下のようにマージし、環境固有のcommonが優先されるようになっています。

# envs/dev/database/terragrunt.hcl
include {
  path = find_in_parent_folders("root.hcl")
}

terraform {
  source = "../../../modules/database"
}

locals {
  # 全環境共通の定義を読み込み
  common_databases_path  = "${get_repo_root()}/terraform/snowflake/common/databases.yaml"
  common_databases_yaml  = yamldecode(file(local.common_databases_path))
  common_databases       = lookup(local.common_databases_yaml, "databases", {})

  # dev環境専用の定義を読み込み
  dev_databases_path  = "${get_repo_root()}/terraform/snowflake/envs/dev/common/databases.yaml"
  dev_databases_yaml  = yamldecode(file(local.dev_databases_path))
  dev_databases       = lookup(local.dev_databases_yaml, "databases", {})

  # 全環境共通 + dev環境固有をマージ(dev側が優先)
  databases = merge(
    local.common_databases,
    local.dev_databases
  )
}

inputs = {
  databases = local.databases
}

本番環境に存在する既存リソースを Terraform で import する手順

Snowflakeを既に運用している場合、既存のリソース(Database、Schema、Warehouse等)をTerraformの管理下(state管理)に置くterraform importを実行していく必要があります。これがだいぶしんどい作業で多くの企業で障壁になっている部分かと思います。実際にimportする際に取り組んだ手順を紹介していきます。

importフロー

  1. Step 1: Terraform定義を作成
  2. Step 2: Terraform import 実行
  3. Step 3: PR 作成 GitHub Actionsからapply

Step 1: Terraform定義を作成

Snowflake CLI でリソース情報を取得します。(例:Database)

#(例:Database)
snow connection set-default prod
snow sql -q "SHOW DATABASES;" --format json > prod_databases.json

JSON を AI Agent(例:Cursor)に渡して Terraformの定義 を生成します。

※Cursorを使っていますが、何でもいいです。あくまでやり方の一例として見てください。
Cursorのチャットで以下のように依頼します。

@prod_databases.json を見て、
common/databases.yaml に追加すべき定義を生成してください。

AI Agentが以下のような定義を生成します。

# 生成例
databases:
  production_db:
    name: "PRODUCTION_DB"
    comment: "本番データベース"
    data_retention_time_in_days: 7
    is_transient: false
  analytics_db:
    name: "ANALYTICS_DB"
    comment: "分析用データベース"
    data_retention_time_in_days: 7
    is_transient: false

Step 2: Terraform import 実行

YAMLファイルとJSONファイルをAI Agentに渡し、YAMLファイルからimportコマンドを生成していきます。
Cursorのチャットで以下のように依頼します。

@common/databases.yaml と @prod_databases.json を見て、
terragrunt importコマンドを生成してスクリプトファイルに保存してください。

生成したスクリプトファイルを実行して本番環境のリソースのimportを行います。

cd envs/prod/database
./import_database.sh

importが終わったら、本番に対してplanを実行し、「No changes. Your infrastructure matches the configuration.」が確認できれば成功です。差分が出た場合は、YAMLファイルを修正して再度planを実行します。差分が完全になくなるまで繰り返します。このとき絶対にprodにapplyしないように徹底します。
import が迷子になる場合、Terraform Registry の該当リソースページを AI に渡すと精度が上がったような気がしました。

https://registry.terraform.io/providers/snowflakedb/snowflake/latest/docs

終わったら、ローカル環境から開発環境にapplyを実行し、applyが通ることを確認します(planが通ってもapplyが通らないことは結構あるので、開発環境でのapply確認をおすすめします)。ステージング環境にplanを実行し、本番環境で作られるリソースのイメージをつけておきます。

Step 3: PR 作成 GitHub Actionsからapply

開発が終わったらPRを作成して、レビューしてもらいます。プルリク公開時に Terraform Plan を自動実行して、結果をGitHubのコメントに書いてくれるtfcmtを利用しようと思ったのですが、Terragrunt のような複数の Statefile を管理するリポジトリは、プルリク上に大量にコメントが流れてしまいます。そのためtfcmtの結果をGitHub ActionsのSummary側に寄せて変更のあったリソースのみ表示できるようにしました。


tfcmtの結果をGHAのjob summaryに表示する

その後、develop にマージされると、stg / dev に自動 applyするGitHub Actionsが走るようになっています。

※tfcmtの結果をGitHub ActionsのSummary側に寄せるのは下記を参考にさせていただきました。
https://qiita.com/tentsauna1117/items/aef1f377fb4d33e2e11c

これにて一連のimport作業は終了です。後はすべてのリソースに対して、ひたすらimportをしていくのみです。気合と根性でやり切りましょう。

最後に

データ基盤のIaC化は、一見するとビジネス価値が見えにくい取り組みかもしれません。しかし、組織のスケーラビリティ変更の安全性運用の効率性という観点では、長期的に大きな価値を生み出す投資です。特にAI開発が主流になった昨今では、基盤のIaC化は早期に取り組むほど恩恵は大きくなります。

この記事が、同様の課題を抱えている組織の参考になれば幸いです。IaC化の検討段階から実装まで、どのフェーズでもお気軽にご相談いただければと思います。

宣伝

株式会社 GA technologies は今回ご紹介したような技術も活用しつつ、アナログな業界をテクノロジーで前進させていく会社です。

データチームでは一緒に働く仲間を大募集中ですので、ぜひご応募ください!

https://herp.careers/v1/gatech/requisition-groups/3996f4ab-1628-43ac-9a29-8c43e627a3da

https://note.ga-tech.co.jp/magazines

参考リンク

GitHubで編集を提案
株式会社GA technologies

Discussion