SnowflakeをTerraformで構成管理してみた

[Snowflakeのカレンダー | Advent Calendar 2022 - Qiita](https://qiita.com/advent-calendar/2022/snowflake) の4日目の記事です。

# これはなに？
- Snowflakeの構成管理をTerraformでやってみたので、その意図や感想等を書き連ねる記事
    - 主にディレクトリ構成について語る記事に仕上がった気がする

# 前提条件

## Snowflakeとは？

https://www.snowflake.com/?lang=ja

- みんな大好きクラウドサービスとしてのデータプラットフォーム
    - [Redshift](https://aws.amazon.com/jp/redshift/) や [BigQuery](https://cloud.google.com/bigquery?hl=ja) が類似製品として挙げられるけど、データウェアハウス用途だけではないので似て非なるもの（と個人的に思っている）
- 今回Snowflakeで構築するシステムは下記のETLのようなものを想定
    - Aシステム（AWS S3）からデータを取得して格納する
    - 格納したデータを良さげに変換してBシステム(AWS S3)に出力する
    - バッチジョブもSnowflakeのTASK機能で実現し、Snowflakeで完結するシステムにする

## Terraformとは？

https://www.terraform.io/

- みんな大好きIaC
    - AWS構成管理やGCP構成管理に用いられるのはよく目にするけど、Snowflakeの構成管理にどれくらい使われているのかは正直不明
- Terraformのproviderは [Snowflake-Labs/snowflakeのv0.40.0](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/0.40.0) を利用する
    - 2022/12/04時点でGAされてないためご利用は計画的に

# Terraformディレクトリ構成

まずは、Terraformのディレクトリ構成をご紹介。

```
.
├ terraform/
  ├ .aws/
  ├ src/
    ├ .terraform
    ├ .terraform.lock.hcl
    ├ external_function.tf
    ├ main.tf
    ├ procedure.tf
    ├ schema.tf
    ├ stage.tf
    ├ table.tf
    ├ task.tf
    ├ terraform.tfvars
    ├ user_function.tf
    ├ variables.tf
```

※一部省略して記載。
※backendをs3に設定しているため `.aws` が存在。

# ポイントその1: 管理対象をDB配下のオブジェクトのみに絞った

ファイル名から分かる通り、Terraformでの管理対象をスキーマやテーブル、ストアドプロシージャなどのDB配下のオブジェクトに絞り、オブジェクト種別毎にファイルを切り分ける形を採用した。

## 管理対象をDB配下のオブジェクトに絞った意図

まず、TerraformからSnowflakeを操作する際、 `main.tf` 内でSnowflake操作時のロールを指定する必要がある。

さらに、開発者が例えば `SYSADMIN` ロールを使って対象DB配下のオブジェクトを操作するようなことは避けたかったため、DB毎に対象DBに対するAdminロールを切りたかった。
開発者は基本的に対象DBのAdmin権限さえあれば十分にシステム構築できるし、開発者が関係ないDBに触るような危険性や、DBやそれより上位のオブジェクト（ユーザや外部ストレージ統合など）を誤って変更する危険性を減らしたい想いもあったので、DB毎にAdminロールを作成した。

以上の理由から、Terraformで利用するのは対象DBのAdminロールとし、それによりTerraformで管理できるものはDB配下のオブジェクトに限られた。

ちなみに、一番変更頻度が高いのがDB配下のオブジェクトだったため、それらだけでもTerraformで管理できたのは結構良かった。
変更前に `terraform plan` で差分を確認したり、Git管理してバージョン管理したり、などなどTerraform導入による恩恵を享受できた。

## 問題点その1: ファイルが肥大化する

しかし、上記構成には問題点も多々ある。

1つにはファイルが肥大化することが挙げられ、特にスキーマが追加された際に顕著に現れる。
例えばテーブル定義であれば `table.tf` 内にすべて記載しているので、スキーマが追加されるとごっそり記述量が増えるのが現状である。
（もちろん、スキーマ追加時以外でもテーブルが増えて記述量が増えるが、一気に増えるのはやはりスキーマ追加時かと）

## 問題点その2: 複数DBの管理をどうするか

上記構成では1つのDBしか管理できないので、複数DBをいかに管理するか問題がある。
これは基本的には、諦めてDB毎にディレクトリを分ける方針にしようと考えている。
（そしてどうせDB毎にディレクトリを分けるのであれば、別のディレクトリ構成案もあるのではないかと考えた将来構想を後述する）
 
# Terraformディレクトリ構成（改善案）

前述の問題や、そもそもDB自体もTerraform管理下にしたい想いもあったので、今後は下記のようなディレクトリ構成に変えていくのが良さそうと考えている。
（以下、まだ試してない話のため、想定通りにいかない可能性もある）

## ライフサイクリに応じたディレクトリ構成＆DBもTerraform管理下に含める

改善案として、ライフサイクルに応じてディレクトリを分ける（=tfstateを分ける）を挙げたい。

```shell
.
├ terraform/
  ├ src/
    ├ main.tf # システムロール `SYSADMIN` を指定
    ├ db.tf
    ├ main_db/
      ├ main.tf # カスタムロール `MAIN_DB_ADMIN` を指定
      ├ schema.tf
      ├ public_schema/
        ├ main.tf # カスタムロール `MAIN_DB_ADMIN` を指定（スキーマ毎にロール切るのも一案？）
        ├ external_function.tf
        ├ procedure.tf
        ├ stage.tf
        ├ table.tf
        ├ task.tf
        ├ user_function.tf
      ├ mobile_app_schema/
        ├ main.tf # カスタムロール `MAIN_DB_ADMIN` を指定
        ├ table.tf
      ├ web_app_schema/
        ├ main.tf # カスタムロール `MAIN_DB_ADMIN` を指定
        ├ table.tf
    ├ sub_db/
      ├ main.tf # カスタムロール `SUB_DB_ADMIN` を指定
      ├ schema.tf
      ├ public_schema/
        ├ main.tf  # カスタムロール `SUB_DB_ADMIN` を指定
        ├ table.tf
```

※省略しているが、各 `main.tf` とセットで `variables.tf` `terraform.tfvars` も設置する想定。

上記によって、DB配下のオブジェクトに加えてDBまで管理することが可能となり、1ファイルの肥大化もある程度抑制可能となりそう。

## Terraform管理対象をさらに増やす

ユーザやロール、権限付与まわり、ストレージ統合やAPI統合もTerraform管理下におきたいとなると、また違ったディレクトリ構成となりそう。
例えば下記案がある。

```shell
.
├ terraform/
  ├ sysadmin/
    ├ main.tf # システムロール `SYSADMIN` を指定
    ├ db.tf
    ├ main_db/
  ├ accountadmin/
    ├ main.tf # システムロール `ACCOUNTADMIN` を指定
    ├ api_integration.tf
    ├ storage_integration.tf
  ├ useradmin/
    ├ main.tf # システムロール `USERADMIN` を指定
    ├ user.tf
    ├ role.tf
  ├ securityadmin/
    ├ main.tf # システムロール `SECURITYADMIN` を指定
    ├ role_grant.tf
```

なお、こちらの案は全く練られていないので参考にしないほうが良いし、そもそもTerraform管理下におくのが良いのか怪しく思っている節もある……

例えば、API統合やストレージ統合を作成する際、Snowflake側だけでなくAWS（やGCPやAzure）側でも操作が必要かつ、SnowflakeとAWS間で交互に操作して作成していく流れがあった記憶なので、Terraformで上手く管理できるか読めない。
（すでに作成済みのものをimportするだけならできそう）

# さいごに

管理対象やディレクトリ構成がまだ見えていなかったり、そもそもTerraformを使うのが適切なのか判断ついていなかったりと、考慮すべき点は盛りだくさん。
とはいえ、個人的にはTerraform用いることでSnowflake構成管理の負担は軽減されたので、選択肢のひとつとして持っておくのは良さそう。
（まだSnowflake用providerはGAされてないけど）

他にも色々と語りたい内容（※1）はあるけれど、今回はひとまずこの辺にて。

---

※1: 以下、色々と語りたい内容。

- 開発環境・検証環境・本番環境をどのように管理したか
    - 今回は諸事情により、1つのSnowflakeアカウント内にDBを切り分ける形で開発・検証・本番環境を設けた
    - Terraformのworkspace機能を使うことで上記3環境の管理コストを減らせた
        - ちなみにworkspace機能はこのような環境管理用途で用意されたものではないが、今回のケースにおいては便利だったので用いた
- AWS S3をbackendに設定するにあたり、Terraform実行時のAWSのMFA認証を如何にして突破したか
    - aws-vaultを用いて実現した