📘

せっかくSnowflake構築するのならTerraform(Terragrunt) で構築しよう【その①】※Quickstart体験

に公開

はじめに

こんにちは。kayoです。
今日はTerraformについて学んでいきます。
Terraformは、HashiCorp社により開発されているオープンソースのツールであり、インフラリソースをコードで管理するInfrastructure as Code(IaC)ツールです。
Snowflakeのオブジェクト(DB・スキーマ・ロールなど)もTerraformを利用して構築することがデファクトスタンダードとなっています。

本記事ではTerraform初学者向けにQuickstartを通してTerraformでのリソース作成手順をご紹介したいと思います。

https://zenn.dev/churadata/articles/6edfed9d597e58

対象者

  • これからTerraformを学ぶ方
  • SnowflakeのオブジェクトをTerraformで構築しようとしてる方

本記事でやらないこと

  • そもそもSnowflakeとは?の説明
  • Snowflakeトライアル作成
  • 手動でSnowflakeオブジェクト作成
  • 冒頭の注釈でも書きましたが「Terragrunt」は本記事ではやりません..

環境について

  • Terraform :1.15.2
  • Terraform Snowflake Provider :2.16.0
  • Snowflake:30日間利用可能なトライアル
  • ローカル環境:Windows 11 Pro端末からTerraform実行

ドキュメント

https://docs.snowflake.com/ja/user-guide/terraform
https://registry.terraform.io/providers/snowflakedb/snowflake/latest/docs

Terraformの仕組み

Terraformはコマンドラインツールです。Windows、masOS、Linux向けにバイナリが用意されています。マルチクラウドに対応しておりAmazon Web Services、Microsoft Azure、Google Cloudと言ったメジャークラウドや、SnowflakeなどのSaaS向けにも対応しています。
Terraformコマンドを実行することでAPIを通して、対象のクラウドサービスにリソースを作成することができます。各クライドサービス毎にプロバイダーと呼ばれるプラグインが用意されています。例えば、AWSであればAWSプロバイダーといった形で本記事では、Snowflakeプロバイダーを利用します。

手動 vs Terraformのリソース管理メリット・デメリット

Terraform でリソースを構築すると、コードの定義通りに DB・スキーマ等を作成できます。手動構築の場合、作業手順書があっても毎回同じ作業を繰り返す必要がありますが、Terraform ではコードを実行するだけで同じリソースを何度でも再現できます。

たとえば、開発環境と同じリソースを本番環境に構築したい場合、環境に合わせたフォルダ・設定ファイルを用意するだけで、手動構築より「圧倒的に速く」かつ「正確に」本番環境を構築できます。

また、Terraform でリソースを管理すると属人化を防ぎやすく、担当者が変わっても引き継ぎがしやすいというメリットもあります。これは私自身の経験からも実感しています。

手法 メリット デメリット
手動構築 実行時間が短く、SQLを直接実行できるため緊急対応に強い 人的ミスが発生しやすい。台帳でリソース管理しても更新が追いつかず、実態との差分が生まれやすいため再現性が低く運用コストが高い
Terraform構築 再現性が高くバージョン管理も容易なため、長期的に作業効率が向上する 初期構築にコード作成の工数がかかる。既存システムを全て手動で構築している場合、Terraform への移行は相当な工数が必要

既存システムからTerraformに移行するのはなかなか骨が折れる作業だと思っています。途中からIaC化するポイントについては以下スライドが大変分かりやすく勉強になりましたので見てみてください。
https://speakerdeck.com/ktatsuya/data-superhero-setusiyon-snowflakeji-pan-wotu-zhong-karaiachua-suru-terraform-x-terragrunt-x-huan-jing-fen-li-she-ji

Terraformのメリットが分かったところで、Quickstartをやってみます。

Quickstart

SnowflakeのQuickstartは2026年5月12時点で以下2つがありました。
本記事では1つ目の「Terraforming Snowflake」を試してみます。
https://www.snowflake.com/en/developers/guides/terraforming-snowflake/
https://www.snowflake.com/en/developers/guides/devops-dcm-terraform-github/

ここからはQuickstartにそって作業をしていきます。

事前に必要なもの

  • Snowflakeアカウント -必要に応じてトライアルアカウントを作成してください
  • GitHubアカウント​
  • gitコマンドラインクライアント
  • お好みのテキストエディタ
  • Terraformがマシンにインストールされていること
SSHキー作成手順はこちら
--SSHキー作成
ssh-keygen -t ed25519 -C "your_email@example.com"

--公開鍵を表示してコピーする
cat ~/.ssh/id_ed25519.pub

-- GitHubに登録
GitHubを開いて、 Settings → SSH and GPG keys から 「New SSH key」 にcatで表示された公開鍵を貼り付け

--接続確認
ssh -T git@github.com

SnowflakeアカウントをTerraformで構築するための空のリポジトリを作成

GitHubは後続手順で使用するため先に作成しましょう

空のリポジトリ作成はこちら
--git用Directory作成
$ mkdir sfguide-terraform-sample
$ cd sfguide-terraform-sample

--README作成
$ echo "# sfguide-terraform-sample" >> README.md

--カレントディレクトリをGitリポジトリとして初期化(.gitフォルダが作成される)
$ git init

--README.mdをステージングエリアに追加(次のコミット対象にする)
$ git add README.md
$ git commit -m "first commit"
--現在のブランチ名をmainに強制リネーム(-Mは既存名があっても上書き)
$ git branch -M main
--リモートリポジトリ(GitHub)をoriginという名前で登録
$ git remote add origin git@github.com:YOUR_GITHUB_USERNAME/sfguide-terraform-sample.git
--mainブランチをリモートにプッシュ。-uで以降git pushだけで同じ先に送れるようになる
$git push -u origin main

Terraform 用のサービス ユーザーを作成

普段Snowsightにログインする用のユーザーとは別にTerraform操作用のserviceユーザーを作成します。

Terraform 用のサービス ユーザーを作成はこちら

Terraformユーザー作成の前に先に認証用のRSAキーを作成します。
Terraformで使用するサービスアカウントを認証するために使用する秘密鍵と公開鍵の作成です。

$ cd ~/.ssh
$ openssl genrsa 2048 | openssl pkcs8 -topk8 -inform PEM -out snowflake_tf_snow_key.p8 -nocrypt
$ openssl rsa -in snowflake_tf_snow_key.p8 -pubout -out snowflake_tf_snow_key.pub

作成された公開鍵「snowflake_tf_snow_key.pub」の内容を全てコピーして
serviceユーザーを手動で作成します。実行はquickstartに従ってACCOUNTADMINを指定。

USE ROLE ACCOUNTADMIN;

CREATE USER TERRAFORM_SVC
    TYPE = SERVICE
    COMMENT = "Service user for Terraforming Snowflake"
    RSA_PUBLIC_KEY = "<RSA_PUBLIC_KEY_HERE>";

--SYSADMINとSECURITYADMINを継承する
GRANT ROLE SYSADMIN TO USER TERRAFORM_SVC;
GRANT ROLE SECURITYADMIN TO USER TERRAFORM_SVC;

Quickstartには「セキュリティ上の重要なベストプラクティスとして、本番環境では、公開鍵もHashiCorp Vault、Azure Key Vault、AWS Secrets Managerなどのシークレット管理ソリューションで保護する必要があります」と記載されていました。この鍵管理については別の機会に調べてみようと思います。

Terraform認証の設定

TerraformがSnowflakeアカウント上でユーザーとして認証できるように、プロバイダー情報をTerraformに渡す必要があります。
Snowsightで以下を実行して組織名「current_organization_name」と現在のアカウント名「current_account_name」を取得します

SELECT LOWER(current_organization_name()) as your_org_name, LOWER(current_account_name()) as your_account_name;

結果

ここまで出来たらTerraformのコードを書いていきます。まずは「main.tf」を作成します。
任意の場所にsnowflakeディレクトリを作成してその配下に格納します。
以下コードをmain.tfに貼り付けます。

\snowflake\main.tf
terraform {
  required_providers {
    snowflake = {
      source = "snowflakedb/snowflake"
    }
  }
}

locals {
  organization_name = "your_org_name"  ##current_organization_nameを記載
  account_name      = "your_account_name"  ##current_account_nameを記載
  private_key_path  = "~/.ssh/snowflake_tf_snow_key.p8"
}

provider "snowflake" {
    organization_name = local.organization_name
    account_name      = local.account_name
    user              = "TERRAFORM_SVC"
    role              = "SYSADMIN"
    authenticator     = "SNOWFLAKE_JWT"
    private_key       = file(local.private_key_path)
}

リソースの宣言

同じmain.tfファイルにデータベースとウェアハウスの設定を2つ追加します。

  • データベース名:tf_db
  • ウェアハウス名:tf_warehouse

先ほどの「main.tf」ファイルに末尾に以下コードを貼り付けます。
(実際に運用するコードでは、複数のリソースは同一のファイルに記載するのではなく別々のファイルに記載する方が管理しやすいと思いますが、今回はQuickstartの通りに実施します。Quickstartにも「全てを1つのファイルに保存する必要はない」旨が記載されていました)

main.tf
resource "snowflake_database" "tf_db" {
  name         = "TF_DEMO_DB"
  is_transient = false
}

resource "snowflake_warehouse" "tf_warehouse" {
  name                      = "TF_DEMO_WH"
  warehouse_type            = "STANDARD"
  warehouse_size            = "XSMALL"
  max_cluster_count         = 1
  min_cluster_count         = 1
  auto_suspend              = 60
  auto_resume               = true
  enable_query_acceleration = false
  initially_suspended       = true
}

プロジェクト実行の準備

Terraformを実行するようにプロジェクトを設定するには、まずプロジェクトを初期化する必要があります。
プロジェクトフォルダ内のターミナルで、以下のコマンドを実行します。

terraform init

「Terraform has been successfully initialized!」が表示されれば初期化成功です。

Warning:部分は、Windows環境でのみ.terraform.lock.hclが生成されたという警告が表示されています。他のOS(Linux/Mac)でも使う場合は追加が必要ですが、Windows環境のみで使うなら無視して問題ありません。
他のOSでも使う場合は以下を実行して、依存関係ロックファイル.terraform.lock.hclに、プロバイダーの選択情報を追加することができます。

terraform providers lock \
  -platform=windows_amd64 \ # 64-bit Windows
  -platform=darwin_amd64 \  # 64-bit macOS
  -platform=linux_amd64     # 64-bit Linux

.terraform.lock.hclについては公式ドキュメントはこちら
https://developer.hashicorp.com/terraform/cli/commands/providers/lock
こちらの記事も分かりやすかったです。
https://dexall.co.jp/articles/?p=2328

少し話がそれましたが、ローカル端末の作業場所を見てみると、
Terraformを実行するために必要なファイルが作成されています。

この.terraformフォルダには、すべてのプロバイダとモジュールの依存関係が格納されています。重要なものは含まれれていないため、.gitignoreに追加してGitHubにpushされないようにします。
Quickstartには.gitignoreには以下3つを追加するよう記載されています。

*.terraform*
*.tfstate
*.tfstate.*

変更内容をソース管理にコミットする

次に、新しいブランチを作成してここまでのコードをpushします。その後Pull Requestを通して対象のブランチにコードを取り込みます。

--現在のブランチから dbwh という新しいブランチを作成
$ git checkout -b dbwh
--カレントディレクトリ以下の全変更ファイルをステージング(コミット対象)に追加する
$ git add .
--ステージングされた変更をメッセージ付きでコミットする
$ git commit -m "Add Config, Database and Warehouse"
--現在のブランチ(dbwh)をリモート origin にpush(アップロード)する
$ git push origin HEAD

Terraformで適用する前に以下コマンドを実行して現在の状態と変更予定分の差分を確認します

terraform plan

結果

Terraform planの結果はこちら

主な項目

  • snowflake_database.tf_db will be created:データベースの作成
  • name:実際にSnowflakeに作成されるオブジェクトの名前
  • Plan: 2 to add, 0 to change, 0 to destroy.:「add」は作成されるリソースの数
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # snowflake_database.tf_db will be created
  + resource "snowflake_database" "tf_db" {
      + catalog                                       = (known after apply)
      + data_retention_time_in_days                   = (known after apply)
      + default_ddl_collation                         = (known after apply)
      + enable_console_output                         = (known after apply)
      + external_volume                               = (known after apply)
      + fully_qualified_name                          = (known after apply)
      + id                                            = (known after apply)
      + is_transient                                  = false
      + log_level                                     = (known after apply)
      + max_data_extension_time_in_days               = (known after apply)
      + name                                          = "TF_DEMO_DB"
      + quoted_identifiers_ignore_case                = (known after apply)
      + replace_invalid_characters                    = (known after apply)
      + storage_serialization_policy                  = (known after apply)
      + suspend_task_after_num_failures               = (known after apply)
      + task_auto_retry_attempts                      = (known after apply)
      + trace_level                                   = (known after apply)
      + user_task_managed_initial_warehouse_size      = (known after apply)
      + user_task_minimum_trigger_interval_in_seconds = (known after apply)
      + user_task_timeout_ms                          = (known after apply)
    }

  # snowflake_warehouse.tf_warehouse will be created
  + resource "snowflake_warehouse" "tf_warehouse" {
      + auto_resume                         = "true"
      + auto_suspend                        = 60
      + enable_query_acceleration           = "false"
      + fully_qualified_name                = (known after apply)
      + id                                  = (known after apply)
      + initially_suspended                 = true
      + max_cluster_count                   = 1
      + max_concurrency_level               = (known after apply)
      + min_cluster_count                   = 1
      + name                                = "TF_DEMO_WH"
      + parameters                          = (known after apply)
      + query_acceleration_max_scale_factor = -1
      + show_output                         = (known after apply)
      + statement_queued_timeout_in_seconds = (known after apply)
      + statement_timeout_in_seconds        = (known after apply)
      + warehouse_size                      = "XSMALL"
      + warehouse_type                      = "STANDARD"
    }

Plan: 2 to add, 0 to change, 0 to destroy.

Terraformを実行する

いよいよTerraform でリソースを作成します。以下コマンドを実行します。

terraform apply

はじめにterraform planと同じ用な結果が表示されます。
主な項目を確認して想定通りであれば、Enter a valueの部分で「yes」を入力します。

結果

Terraform applyの結果はこちら

主な項目

  • snowflake_database.tf_db will be created:データベースの作成
  • name:実際にSnowflakeに作成されるオブジェクトの名前
  • Plan: 2 to add, 0 to change, 0 to destroy.:「add」は作成されるリソースの数
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # snowflake_database.tf_db will be created
  + resource "snowflake_database" "tf_db" {
      + catalog                                       = (known after apply)
      + data_retention_time_in_days                   = (known after apply)
      + default_ddl_collation                         = (known after apply)
      + enable_console_output                         = (known after apply)
      + external_volume                               = (known after apply)
      + fully_qualified_name                          = (known after apply)
      + id                                            = (known after apply)
      + is_transient                                  = false
      + log_level                                     = (known after apply)
      + max_data_extension_time_in_days               = (known after apply)
      + name                                          = "TF_DEMO_DB"
      + quoted_identifiers_ignore_case                = (known after apply)
      + replace_invalid_characters                    = (known after apply)
      + storage_serialization_policy                  = (known after apply)
      + suspend_task_after_num_failures               = (known after apply)
      + task_auto_retry_attempts                      = (known after apply)
      + trace_level                                   = (known after apply)
      + user_task_managed_initial_warehouse_size      = (known after apply)
      + user_task_minimum_trigger_interval_in_seconds = (known after apply)
      + user_task_timeout_ms                          = (known after apply)
    }

  # snowflake_warehouse.tf_warehouse will be created
  + resource "snowflake_warehouse" "tf_warehouse" {
      + auto_resume                         = "true"
      + auto_suspend                        = 60
      + enable_query_acceleration           = "false"
      + fully_qualified_name                = (known after apply)
      + id                                  = (known after apply)
      + initially_suspended                 = true
      + max_cluster_count                   = 1
      + max_concurrency_level               = (known after apply)
      + min_cluster_count                   = 1
      + name                                = "TF_DEMO_WH"
      + parameters                          = (known after apply)
      + query_acceleration_max_scale_factor = -1
      + show_output                         = (known after apply)
      + statement_queued_timeout_in_seconds = (known after apply)
      + statement_timeout_in_seconds        = (known after apply)
      + warehouse_size                      = "XSMALL"
      + warehouse_type                      = "STANDARD"
    }

Plan: 2 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

snowflake_database.tf_db: Creating...
snowflake_warehouse.tf_warehouse: Creating...
snowflake_database.tf_db: Creation complete after 0s [id=TF_DEMO_DB]
snowflake_warehouse.tf_warehouse: Creation complete after 0s [id=TF_DEMO_WH]

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

末尾に「Apply complete! Resources: 2 added, 0 changed, 0 destroyed.」と表示されれば成功です。「Resources: 2 」の部分で2つのリソースが作成されたことが分かります。

Terraform apply実行後の確認

Snowsightを開き、データベースとウェアハウスが作成されたことを確認します。
ちゃんと作成されていますね!!
データベース

ウェアハウス

リソースの変更と追加

Terraformで作成したリソースについてパラメータの変更をしたり、追加をしてみます。
具体的には、以下のようにmain.tfの内容を変更したり、追加したりします

  • main.tfの「snowflake_warehouse」ブロックのwarehouse_typeを「XSMALL」を「SMALL」に変更
  • main.tfの末尾に追加したいリソースの設定を追加する
    • スキーマ名:TF_DEMO_SC
    • ロール名:TF_DEMO_ROLE
    • ロールの継承関係:SYSADMINはTF_DEMO_ROLEを継承して親ロールをSYSADMINとする
    • 権限:DB、スキーマのusage。既存のテーブルと未来のテーブルに対するselect
    • ユーザー名:TF_DEMO_USER

コードの意味については、Snowflakeオブジェクト作成SQLが分かればだいたい理解できそうです。

main.tf
resource "snowflake_warehouse" "tf_warehouse" {
  name                      = "TF_DEMO_WH"
  warehouse_type            = "STANDARD"
  warehouse_size            = "SMALL"
  max_cluster_count         = 1
  min_cluster_count         = 1
  auto_suspend              = 60
  auto_resume               = true
  enable_query_acceleration = false
  initially_suspended       = true
}

# Create a new schema in the DB
resource "snowflake_schema" "tf_db_tf_schema" {
  name                = "TF_DEMO_SC"
  database            = snowflake_database.tf_db.name
  with_managed_access = false  #false:所有者が権限を管理する
}

# New provider that will use USERADMIN to create users, roles, and grants
provider "snowflake" {
    organization_name = local.organization_name
    account_name      = local.account_name
    user              = "TERRAFORM_SVC"
    role              = "USERADMIN"
    alias             = "useradmin"
    authenticator     = "SNOWFLAKE_JWT"
    private_key       = file(local.private_key_path)
}

# Create a new role using USERADMIN
resource "snowflake_account_role" "tf_role" {
    provider          = snowflake.useradmin
    name              = "TF_DEMO_ROLE"
    comment           = "My Terraform role"
}

# Grant the new role to SYSADMIN (best practice)
resource "snowflake_grant_account_role" "grant_tf_role_to_sysadmin" {
    provider         = snowflake.useradmin
    role_name        = snowflake_account_role.tf_role.name
    parent_role_name = "SYSADMIN" #作成するロールの親ロールにSYSADMINを指定
}

# Create a key for the new user
resource "tls_private_key" "svc_key" {
    algorithm = "RSA"
    rsa_bits  = 2048
}

# Create a new user
resource "snowflake_user" "tf_user" {
    provider          = snowflake.useradmin
    name              = "TF_DEMO_USER"
    default_warehouse = snowflake_warehouse.tf_warehouse.name
    default_role      = snowflake_account_role.tf_role.name
    default_namespace = "${snowflake_database.tf_db.name}.${snowflake_schema.tf_db_tf_schema.fully_qualified_name}" #ログイン時にユーザーセッションでデフォルトでアクティブになる名前空間(データベース,スキーマのみ)を指定 
    rsa_public_key    = substr(tls_private_key.svc_key.public_key_pem, 27, 398) #ユーザーのRSA公開鍵を指定
}

# Grant the new role to the new user
resource "snowflake_grant_account_role" "grants" {
    provider          = snowflake.useradmin
    role_name         = snowflake_account_role.tf_role.name
    user_name         = snowflake_user.tf_user.name
}

ロールに対しての権限を設定します。

main.tf
# Grant usage on the database
resource "snowflake_grant_privileges_to_account_role" "grant_usage_tf_db_to_tf_role" {
    provider          = snowflake.useradmin
    privileges        = ["USAGE"]
    account_role_name = snowflake_account_role.tf_role.name
    on_account_object {
        object_type = "DATABASE"
        object_name = snowflake_database.tf_db.name
  }
}

# Grant usage on the schema
resource "snowflake_grant_privileges_to_account_role" "grant_usage_tf_db_tf_schema_to_tf_role" {
    provider          = snowflake.useradmin
    privileges        = ["USAGE"]
    account_role_name = snowflake_account_role.tf_role.name
    on_schema {
        schema_name = snowflake_schema.tf_db_tf_schema.fully_qualified_name
  }
}

# Grant select on all tables in the schema (even if the schema is empty)
resource "snowflake_grant_privileges_to_account_role" "grant_all_tables" {
    provider          = snowflake.useradmin
    privileges        = ["SELECT"]
    account_role_name = snowflake_account_role.tf_role.name
    on_schema_object {
        all {
            object_type_plural = "TABLES"
            in_schema          = snowflake_schema.tf_db_tf_schema.fully_qualified_name
    }
  }
}

# Grant select on the future tables in the schema
resource "snowflake_grant_privileges_to_account_role" "grant_future_tables" {
    provider          = snowflake.useradmin
    privileges        = ["SELECT"]
    account_role_name = snowflake_account_role.tf_role.name
    on_schema_object {
        future {
            object_type_plural = "TABLES"
            in_schema          = snowflake_schema.tf_db_tf_schema.fully_qualified_name
    }
  }
}

main.tfと同じ階層に新しく「outputs.tf」ファイルを作成します。
作成するユーザーに対して公開鍵・秘密鍵を作成しましたが、そのユーザーが公開鍵を使用してSnowflakeを利用できるようにするには公開鍵の情報が必要ですが、Terraform プロバイダーが利用できる形式エクスポートがサポートされていないようです。よって、outputs.tfファイルを作成してterraform applyコマンド実行後に表示させるようにします。

outputs.tf
output "snowflake_svc_public_key" {
    value     = tls_private_key.svc_key.public_key_pem
}

output "snowflake_svc_private_key" {
    value     = tls_private_key.svc_key.private_key_pem
    sensitive = true
}

ここまで記載したらterraform planしてみます

terraform plan

エラーになりました。tls_private_keyを利用する際には依存関係を更新するため terraform init -upgradeを実行してくださいと言われました。terraform init -upgradeを実行するとhashicorp/tlsがインストールされます。

実行すると成功しました

再度terraform planしてみます
結果

Terraform planの結果はこちら
snowflake_database.tf_db: Refreshing state... [id=TF_DEMO_DB]
snowflake_warehouse.tf_warehouse: Refreshing state... [id=TF_DEMO_WH]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create
  ~ update in-place

Terraform will perform the following actions:

  # snowflake_account_role.tf_role will be created
  + resource "snowflake_account_role" "tf_role" {
      + comment              = "My Terraform role"
      + fully_qualified_name = (known after apply)
      + id                   = (known after apply)
      + name                 = "TF_DEMO_ROLE"
      + show_output          = (known after apply)
    }

  # snowflake_grant_account_role.grant_tf_role_to_sysadmin will be created
  + resource "snowflake_grant_account_role" "grant_tf_role_to_sysadmin" {
      + id               = (known after apply)
      + parent_role_name = "SYSADMIN"
      + role_name        = "TF_DEMO_ROLE"
    }

  # snowflake_grant_account_role.grants will be created
  + resource "snowflake_grant_account_role" "grants" {
      + id        = (known after apply)
      + role_name = "TF_DEMO_ROLE"
      + user_name = "TF_DEMO_USER"
    }

  # snowflake_grant_privileges_to_account_role.grant_all_tables will be created
  + resource "snowflake_grant_privileges_to_account_role" "grant_all_tables" {
      + account_role_name           = "TF_DEMO_ROLE"
      + all_privileges              = false
      + always_apply                = false
      + id                          = (known after apply)
      + on_account                  = false
      + privileges                  = [
          + "SELECT",
        ]
      + strict_privilege_management = false
      + with_grant_option           = false

      + on_schema_object {
          + all {
              + in_schema          = (known after apply)
              + object_type_plural = "TABLES"
            }
        }
    }

  # snowflake_grant_privileges_to_account_role.grant_future_tables will be created
  + resource "snowflake_grant_privileges_to_account_role" "grant_future_tables" {
      + account_role_name           = "TF_DEMO_ROLE"
      + all_privileges              = false
      + always_apply                = false
      + id                          = (known after apply)
      + on_account                  = false
      + privileges                  = [
          + "SELECT",
        ]
      + strict_privilege_management = false
      + with_grant_option           = false

      + on_schema_object {
          + future {
              + in_schema          = (known after apply)
              + object_type_plural = "TABLES"
            }
        }
    }

  # snowflake_grant_privileges_to_account_role.grant_usage_tf_db_tf_schema_to_tf_role will be created
  + resource "snowflake_grant_privileges_to_account_role" "grant_usage_tf_db_tf_schema_to_tf_role" {
      + account_role_name           = "TF_DEMO_ROLE"
      + all_privileges              = false
      + always_apply                = false
      + id                          = (known after apply)
      + on_account                  = false
      + privileges                  = [
          + "USAGE",
        ]
      + strict_privilege_management = false
      + with_grant_option           = false

      + on_schema {
          + schema_name = (known after apply)
        }
    }

  # snowflake_grant_privileges_to_account_role.grant_usage_tf_db_to_tf_role will be created
  + resource "snowflake_grant_privileges_to_account_role" "grant_usage_tf_db_to_tf_role" {
      + account_role_name           = "TF_DEMO_ROLE"
      + all_privileges              = false
      + always_apply                = false
      + id                          = (known after apply)
      + on_account                  = false
      + privileges                  = [
          + "USAGE",
        ]
      + strict_privilege_management = false
      + with_grant_option           = false

      + on_account_object {
          + object_name = "TF_DEMO_DB"
          + object_type = "DATABASE"
        }
    }

  # snowflake_schema.tf_db_tf_schema will be created
  + resource "snowflake_schema" "tf_db_tf_schema" {
      + catalog                                       = (known after apply)
      + data_retention_time_in_days                   = (known after apply)
      + database                                      = "TF_DEMO_DB"
      + default_ddl_collation                         = (known after apply)
      + describe_output                               = (known after apply)
      + enable_console_output                         = (known after apply)
      + external_volume                               = (known after apply)
      + fully_qualified_name                          = (known after apply)
      + id                                            = (known after apply)
      + is_transient                                  = "default"
      + log_level                                     = (known after apply)
      + max_data_extension_time_in_days               = (known after apply)
      + name                                          = "TF_DEMO_SC"
      + parameters                                    = (known after apply)
      + pipe_execution_paused                         = (known after apply)
      + quoted_identifiers_ignore_case                = (known after apply)
      + replace_invalid_characters                    = (known after apply)
      + show_output                                   = (known after apply)
      + storage_serialization_policy                  = (known after apply)
      + suspend_task_after_num_failures               = (known after apply)
      + task_auto_retry_attempts                      = (known after apply)
      + trace_level                                   = (known after apply)
      + user_task_managed_initial_warehouse_size      = (known after apply)
      + user_task_minimum_trigger_interval_in_seconds = (known after apply)
      + user_task_timeout_ms                          = (known after apply)
      + with_managed_access                           = "false"
    }

  # snowflake_user.tf_user will be created
  + resource "snowflake_user" "tf_user" {
      + abort_detached_query                          = (known after apply)
      + autocommit                                    = (known after apply)
      + binary_input_format                           = (known after apply)
      + binary_output_format                          = (known after apply)
      + client_memory_limit                           = (known after apply)
      + client_metadata_request_use_connection_ctx    = (known after apply)
      + client_prefetch_threads                       = (known after apply)
      + client_result_chunk_size                      = (known after apply)
      + client_result_column_case_insensitive         = (known after apply)
      + client_session_keep_alive                     = (known after apply)
      + client_session_keep_alive_heartbeat_frequency = (known after apply)
      + client_timestamp_type_mapping                 = (known after apply)
      + date_input_format                             = (known after apply)
      + date_output_format                            = (known after apply)
      + default_namespace                             = (known after apply)
      + default_role                                  = "TF_DEMO_ROLE"
      + default_secondary_roles_option                = "DEFAULT"
      + default_warehouse                             = "TF_DEMO_WH"
      + disable_mfa                                   = "default"
      + disabled                                      = "default"
      + enable_unload_physical_type_optimization      = (known after apply)
      + enable_unredacted_query_syntax_error          = (known after apply)
      + error_on_nondeterministic_merge               = (known after apply)
      + error_on_nondeterministic_update              = (known after apply)
      + fully_qualified_name                          = (known after apply)
      + geography_output_format                       = (known after apply)
      + geometry_output_format                        = (known after apply)
      + id                                            = (known after apply)
      + jdbc_treat_decimal_as_int                     = (known after apply)
      + jdbc_treat_timestamp_ntz_as_utc               = (known after apply)
      + jdbc_use_session_timezone                     = (known after apply)
      + json_indent                                   = (known after apply)
      + lock_timeout                                  = (known after apply)
      + log_level                                     = (known after apply)
      + mins_to_bypass_mfa                            = -1
      + mins_to_unlock                                = -1
      + multi_statement_count                         = (known after apply)
      + must_change_password                          = "default"
      + name                                          = "TF_DEMO_USER"
      + network_policy                                = (known after apply)
      + noorder_sequence_as_default                   = (known after apply)
      + odbc_treat_decimal_as_int                     = (known after apply)
      + parameters                                    = (known after apply)
      + prevent_unload_to_internal_stages             = (known after apply)
      + query_tag                                     = (known after apply)
      + quoted_identifiers_ignore_case                = (known after apply)
      + rows_per_resultset                            = (known after apply)
      + rsa_public_key                                = (known after apply)
      + s3_stage_vpce_dns_name                        = (known after apply)
      + search_path                                   = (known after apply)
      + show_output                                   = (known after apply)
      + simulated_data_sharing_consumer               = (known after apply)
      + statement_queued_timeout_in_seconds           = (known after apply)
      + statement_timeout_in_seconds                  = (known after apply)
      + strict_json_output                            = (known after apply)
      + time_input_format                             = (known after apply)
      + time_output_format                            = (known after apply)
      + timestamp_day_is_always_24h                   = (known after apply)
      + timestamp_input_format                        = (known after apply)
      + timestamp_ltz_output_format                   = (known after apply)
      + timestamp_ntz_output_format                   = (known after apply)
      + timestamp_output_format                       = (known after apply)
      + timestamp_type_mapping                        = (known after apply)
      + timestamp_tz_output_format                    = (known after apply)
      + timezone                                      = (known after apply)
      + trace_level                                   = (known after apply)
      + transaction_abort_on_error                    = (known after apply)
      + transaction_default_isolation_level           = (known after apply)
      + two_digit_century_start                       = (known after apply)
      + unsupported_ddl_action                        = (known after apply)
      + use_cached_result                             = (known after apply)
      + user_type                                     = (known after apply)
      + week_of_year_policy                           = (known after apply)
      + week_start                                    = (known after apply)
    }

  # snowflake_warehouse.tf_warehouse will be updated in-place
  ~ resource "snowflake_warehouse" "tf_warehouse" {
        id                                  = "TF_DEMO_WH"
        name                                = "TF_DEMO_WH"
      ~ show_output                         = [
          - {
              - auto_resume                         = true
              - auto_suspend                        = 60
              - available                           = 0
              - created_on                          = "2026-05-12 22:11:51.729 +0900 JST"
              - enable_query_acceleration           = false
              - generation                          = "2"
              - is_current                          = false
              - is_default                          = false
              - max_cluster_count                   = 1
              - min_cluster_count                   = 1
              - name                                = "TF_DEMO_WH"
              - other                               = 0
              - owner                               = "SYSADMIN"
              - owner_role_type                     = "ROLE"
              - provisioning                        = 0
              - query_acceleration_max_scale_factor = 8
              - queued                              = 0
              - quiescing                           = 0
              - resumed_on                          = "2026-05-12 22:11:51.738 +0900 JST"
              - running                             = 0
              - scaling_policy                      = "STANDARD"
              - size                                = "XSMALL"
              - started_clusters                    = 0
              - state                               = "SUSPENDED"
              - type                                = "STANDARD"
              - updated_on                          = "2026-05-12 22:11:51.753 +0900 JST"
                # (3 unchanged attributes hidden)
            },
        ] -> (known after apply)
      ~ warehouse_size                      = "XSMALL" -> "SMALL"
        # (14 unchanged attributes hidden)
    }

  # tls_private_key.svc_key will be created
  + resource "tls_private_key" "svc_key" {
      + algorithm                     = "RSA"
      + ecdsa_curve                   = "P224"
      + id                            = (known after apply)
      + private_key_openssh           = (sensitive value)
      + private_key_pem               = (sensitive value)
      + private_key_pem_pkcs8         = (sensitive value)
      + public_key_fingerprint_md5    = (known after apply)
      + public_key_fingerprint_sha256 = (known after apply)
      + public_key_openssh            = (known after apply)
      + public_key_pem                = (known after apply)
      + rsa_bits                      = 2048
    }

Plan: 10 to add, 1 to change, 0 to destroy.

Changes to Outputs:
  + snowflake_svc_private_key = (sensitive value)
  + snowflake_svc_public_key  = (known after apply)

ここまでくるとterraform applyしたくなりますが、先にコードをgitにpushします。

$ git checkout -b feat/objects-and-service-user
$ git add main.tf
$ git add outputs.tf
$ git commit -m "Create Service User, Role, Schema, Grants"
$ git push origin HEAD

terraform applyします
結果

terraform applyの結果はこちら
snowflake_database.tf_db: Refreshing state... [id=TF_DEMO_DB]
snowflake_warehouse.tf_warehouse: Refreshing state... [id=TF_DEMO_WH]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create
  ~ update in-place

Terraform will perform the following actions:

  # snowflake_account_role.tf_role will be created
  + resource "snowflake_account_role" "tf_role" {
      + comment              = "My Terraform role"
      + fully_qualified_name = (known after apply)
      + id                   = (known after apply)
      + name                 = "TF_DEMO_ROLE"
      + show_output          = (known after apply)
    }

  # snowflake_grant_account_role.grant_tf_role_to_sysadmin will be created
  + resource "snowflake_grant_account_role" "grant_tf_role_to_sysadmin" {
      + id               = (known after apply)
      + parent_role_name = "SYSADMIN"
      + role_name        = "TF_DEMO_ROLE"
    }

  # snowflake_grant_account_role.grants will be created
  + resource "snowflake_grant_account_role" "grants" {
      + id        = (known after apply)
      + role_name = "TF_DEMO_ROLE"
      + user_name = "TF_DEMO_USER"
    }

  # snowflake_grant_privileges_to_account_role.grant_all_tables will be created
  + resource "snowflake_grant_privileges_to_account_role" "grant_all_tables" {
      + account_role_name           = "TF_DEMO_ROLE"
      + all_privileges              = false
      + always_apply                = false
      + id                          = (known after apply)
      + on_account                  = false
      + privileges                  = [
          + "SELECT",
        ]
      + strict_privilege_management = false
      + with_grant_option           = false

      + on_schema_object {
          + all {
              + in_schema          = (known after apply)
              + object_type_plural = "TABLES"
            }
        }
    }

  # snowflake_grant_privileges_to_account_role.grant_future_tables will be created
  + resource "snowflake_grant_privileges_to_account_role" "grant_future_tables" {
      + account_role_name           = "TF_DEMO_ROLE"
      + all_privileges              = false
      + always_apply                = false
      + id                          = (known after apply)
      + on_account                  = false
      + privileges                  = [
          + "SELECT",
        ]
      + strict_privilege_management = false
      + with_grant_option           = false

      + on_schema_object {
          + future {
              + in_schema          = (known after apply)
              + object_type_plural = "TABLES"
            }
        }
    }

  # snowflake_grant_privileges_to_account_role.grant_usage_tf_db_tf_schema_to_tf_role will be created
  + resource "snowflake_grant_privileges_to_account_role" "grant_usage_tf_db_tf_schema_to_tf_role" {
      + account_role_name           = "TF_DEMO_ROLE"
      + all_privileges              = false
      + always_apply                = false
      + id                          = (known after apply)
      + on_account                  = false
      + privileges                  = [
          + "USAGE",
        ]
      + strict_privilege_management = false
      + with_grant_option           = false

      + on_schema {
          + schema_name = (known after apply)
        }
    }

  # snowflake_grant_privileges_to_account_role.grant_usage_tf_db_to_tf_role will be created
  + resource "snowflake_grant_privileges_to_account_role" "grant_usage_tf_db_to_tf_role" {
      + account_role_name           = "TF_DEMO_ROLE"
      + all_privileges              = false
      + always_apply                = false
      + id                          = (known after apply)
      + on_account                  = false
      + privileges                  = [
          + "USAGE",
        ]
      + strict_privilege_management = false
      + with_grant_option           = false

      + on_account_object {
          + object_name = "TF_DEMO_DB"
          + object_type = "DATABASE"
        }
    }

  # snowflake_schema.tf_db_tf_schema will be created
  + resource "snowflake_schema" "tf_db_tf_schema" {
      + catalog                                       = (known after apply)
      + data_retention_time_in_days                   = (known after apply)
      + database                                      = "TF_DEMO_DB"
      + default_ddl_collation                         = (known after apply)
      + describe_output                               = (known after apply)
      + enable_console_output                         = (known after apply)
      + external_volume                               = (known after apply)
      + fully_qualified_name                          = (known after apply)
      + id                                            = (known after apply)
      + is_transient                                  = "default"
      + log_level                                     = (known after apply)
      + max_data_extension_time_in_days               = (known after apply)
      + name                                          = "TF_DEMO_SC"
      + parameters                                    = (known after apply)
      + pipe_execution_paused                         = (known after apply)
      + quoted_identifiers_ignore_case                = (known after apply)
      + replace_invalid_characters                    = (known after apply)
      + show_output                                   = (known after apply)
      + storage_serialization_policy                  = (known after apply)
      + suspend_task_after_num_failures               = (known after apply)
      + task_auto_retry_attempts                      = (known after apply)
      + trace_level                                   = (known after apply)
      + user_task_managed_initial_warehouse_size      = (known after apply)
      + user_task_minimum_trigger_interval_in_seconds = (known after apply)
      + user_task_timeout_ms                          = (known after apply)
      + with_managed_access                           = "false"
    }

  # snowflake_user.tf_user will be created
  + resource "snowflake_user" "tf_user" {
      + abort_detached_query                          = (known after apply)
      + autocommit                                    = (known after apply)
      + binary_input_format                           = (known after apply)
      + binary_output_format                          = (known after apply)
      + client_memory_limit                           = (known after apply)
      + client_metadata_request_use_connection_ctx    = (known after apply)
      + client_prefetch_threads                       = (known after apply)
      + client_result_chunk_size                      = (known after apply)
      + client_result_column_case_insensitive         = (known after apply)
      + client_session_keep_alive                     = (known after apply)
      + client_session_keep_alive_heartbeat_frequency = (known after apply)
      + client_timestamp_type_mapping                 = (known after apply)
      + date_input_format                             = (known after apply)
      + date_output_format                            = (known after apply)
      + default_namespace                             = (known after apply)
      + default_role                                  = "TF_DEMO_ROLE"
      + default_secondary_roles_option                = "DEFAULT"
      + default_warehouse                             = "TF_DEMO_WH"
      + disable_mfa                                   = "default"
      + disabled                                      = "default"
      + enable_unload_physical_type_optimization      = (known after apply)
      + enable_unredacted_query_syntax_error          = (known after apply)
      + error_on_nondeterministic_merge               = (known after apply)
      + error_on_nondeterministic_update              = (known after apply)
      + fully_qualified_name                          = (known after apply)
      + geography_output_format                       = (known after apply)
      + geometry_output_format                        = (known after apply)
      + id                                            = (known after apply)
      + jdbc_treat_decimal_as_int                     = (known after apply)
      + jdbc_treat_timestamp_ntz_as_utc               = (known after apply)
      + jdbc_use_session_timezone                     = (known after apply)
      + json_indent                                   = (known after apply)
      + lock_timeout                                  = (known after apply)
      + log_level                                     = (known after apply)
      + mins_to_bypass_mfa                            = -1
      + mins_to_unlock                                = -1
      + multi_statement_count                         = (known after apply)
      + must_change_password                          = "default"
      + name                                          = "TF_DEMO_USER"
      + network_policy                                = (known after apply)
      + noorder_sequence_as_default                   = (known after apply)
      + odbc_treat_decimal_as_int                     = (known after apply)
      + parameters                                    = (known after apply)
      + prevent_unload_to_internal_stages             = (known after apply)
      + query_tag                                     = (known after apply)
      + quoted_identifiers_ignore_case                = (known after apply)
      + rows_per_resultset                            = (known after apply)
      + rsa_public_key                                = (known after apply)
      + s3_stage_vpce_dns_name                        = (known after apply)
      + search_path                                   = (known after apply)
      + show_output                                   = (known after apply)
      + simulated_data_sharing_consumer               = (known after apply)
      + statement_queued_timeout_in_seconds           = (known after apply)
      + statement_timeout_in_seconds                  = (known after apply)
      + strict_json_output                            = (known after apply)
      + time_input_format                             = (known after apply)
      + time_output_format                            = (known after apply)
      + timestamp_day_is_always_24h                   = (known after apply)
      + timestamp_input_format                        = (known after apply)
      + timestamp_ltz_output_format                   = (known after apply)
      + timestamp_ntz_output_format                   = (known after apply)
      + timestamp_output_format                       = (known after apply)
      + timestamp_type_mapping                        = (known after apply)
      + timestamp_tz_output_format                    = (known after apply)
      + timezone                                      = (known after apply)
      + trace_level                                   = (known after apply)
      + transaction_abort_on_error                    = (known after apply)
      + transaction_default_isolation_level           = (known after apply)
      + two_digit_century_start                       = (known after apply)
      + unsupported_ddl_action                        = (known after apply)
      + use_cached_result                             = (known after apply)
      + user_type                                     = (known after apply)
      + week_of_year_policy                           = (known after apply)
      + week_start                                    = (known after apply)
    }

  # snowflake_warehouse.tf_warehouse will be updated in-place
  ~ resource "snowflake_warehouse" "tf_warehouse" {
        id                                  = "TF_DEMO_WH"
        name                                = "TF_DEMO_WH"
      ~ show_output                         = [
          - {
              - auto_resume                         = true
              - auto_suspend                        = 60
              - available                           = 0
              - created_on                          = "2026-05-12 22:11:51.729 +0900 JST"
              - enable_query_acceleration           = false
              - generation                          = "2"
              - is_current                          = false
              - is_default                          = false
              - max_cluster_count                   = 1
              - min_cluster_count                   = 1
              - name                                = "TF_DEMO_WH"
              - other                               = 0
              - owner                               = "SYSADMIN"
              - owner_role_type                     = "ROLE"
              - provisioning                        = 0
              - query_acceleration_max_scale_factor = 8
              - queued                              = 0
              - quiescing                           = 0
              - resumed_on                          = "2026-05-12 22:11:51.738 +0900 JST"
              - running                             = 0
              - scaling_policy                      = "STANDARD"
              - size                                = "XSMALL"
              - started_clusters                    = 0
              - state                               = "SUSPENDED"
              - type                                = "STANDARD"
              - updated_on                          = "2026-05-12 22:11:51.753 +0900 JST"
                # (3 unchanged attributes hidden)
            },
        ] -> (known after apply)
      ~ warehouse_size                      = "XSMALL" -> "SMALL"
        # (14 unchanged attributes hidden)
    }

  # tls_private_key.svc_key will be created
  + resource "tls_private_key" "svc_key" {
      + algorithm                     = "RSA"
      + ecdsa_curve                   = "P224"
      + id                            = (known after apply)
      + private_key_openssh           = (sensitive value)
      + private_key_pem               = (sensitive value)
      + private_key_pem_pkcs8         = (sensitive value)
      + public_key_fingerprint_md5    = (known after apply)
      + public_key_fingerprint_sha256 = (known after apply)
      + public_key_openssh            = (known after apply)
      + public_key_pem                = (known after apply)
      + rsa_bits                      = 2048
    }

Plan: 10 to add, 1 to change, 0 to destroy.

Changes to Outputs:
  + snowflake_svc_private_key = (sensitive value)
  + snowflake_svc_public_key  = (known after apply)

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

tls_private_key.svc_key: Creating...
tls_private_key.svc_key: Creation complete after 0s [id=ef51230a06bc5f429058cb2dcf493785a4e12437]
snowflake_account_role.tf_role: Creating...
snowflake_schema.tf_db_tf_schema: Creating...
snowflake_warehouse.tf_warehouse: Modifying... [id=TF_DEMO_WH]
snowflake_account_role.tf_role: Creation complete after 0s [id=TF_DEMO_ROLE]
snowflake_grant_account_role.grant_tf_role_to_sysadmin: Creating...
snowflake_grant_privileges_to_account_role.grant_usage_tf_db_to_tf_role: Creating...
snowflake_grant_account_role.grant_tf_role_to_sysadmin: Creation complete after 0s [id="TF_DEMO_ROLE"|ROLE|"SYSADMIN"]
snowflake_warehouse.tf_warehouse: Modifications complete after 0s [id=TF_DEMO_WH]
snowflake_schema.tf_db_tf_schema: Creation complete after 0s [id="TF_DEMO_DB"."TF_DEMO_SC"]
snowflake_grant_privileges_to_account_role.grant_all_tables: Creating...
snowflake_grant_privileges_to_account_role.grant_usage_tf_db_tf_schema_to_tf_role: Creating...
snowflake_grant_privileges_to_account_role.grant_future_tables: Creating...
snowflake_grant_privileges_to_account_role.grant_usage_tf_db_to_tf_role: Creation complete after 0s [id="TF_DEMO_ROLE"|false|false|USAGE|OnAccountObject|DATABASE|"TF_DEMO_DB"]
snowflake_user.tf_user: Creating...
snowflake_grant_privileges_to_account_role.grant_usage_tf_db_tf_schema_to_tf_role: Creation complete after 0s [id="TF_DEMO_ROLE"|false|false|USAGE|OnSchema|OnSchema|"TF_DEMO_DB"."TF_DEMO_SC"]
snowflake_grant_privileges_to_account_role.grant_all_tables: Creation complete after 1s [id="TF_DEMO_ROLE"|false|false|SELECT|OnSchemaObject|OnAll|TABLES|InSchema|"TF_DEMO_DB"."TF_DEMO_SC"]
snowflake_grant_privileges_to_account_role.grant_future_tables: Creation complete after 1s [id="TF_DEMO_ROLE"|false|false|SELECT|OnSchemaObject|OnFuture|TABLES|InSchema|"TF_DEMO_DB"."TF_DEMO_SC"]
snowflake_user.tf_user: Creation complete after 1s [id=TF_DEMO_USER]
snowflake_grant_account_role.grants: Creating...
snowflake_grant_account_role.grants: Creation complete after 0s [id="TF_DEMO_ROLE"|USER|"TF_DEMO_USER"]

Apply complete! Resources: 10 added, 1 changed, 0 destroyed.

Outputs:

snowflake_svc_private_key = <sensitive>
snowflake_svc_public_key = <<EOT
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvOEGKcXeK6EztZpab9oW
Whh6tD743NIEa3dHnPfHQ5UnU9PkE250vP4ie+vOBfIqwzWEeyxuZfCWzyh3GB9r
2bM9n/EUupbJQUI6ULBqpc6PF07mRFCwBrZ7Kdb2HE0OrMVWmeFH/2WPKI5mZ+q9
4iPQCBGBzn5jANphZEISBe5wyBUdXYGlc2omEtxRub7rHRzYL7kwCQQPhbmZrCja
bfot10oQ58mmfsDTtMAUUeO2VQmS+/sSCB9tc62+bVEwNnm2jFLMmGCFX7qYPSP8
CWmMMzYIuZl83FSnSNdUxGpZhVlGtvCdrFhhemRYGmS92/7U9HD9gttoAdXaxjhJ
kQIDAQAB
-----END PUBLIC KEY-----

EOT

Snowsight上で作成されたリソースの確認

Terraformで実行したリソースが全てSnowflakeに作成されているか確認します

ウェアハウス
ちゃんとSMALLに修正されています。

SHOW WAREHOUSES ->> SELECT *FROM $1 WHERE "name" = 'TF_DEMO_WH';

ユーザー
ちゃんと作成されています。

SHOW USERS ->> SELECT *FROM $1 WHERE "name" = 'TF_DEMO_USER';


ロール
ロールはUSERADMINで作成したからOWNERがUSERADMINになってますね。指定通りにできててこの辺は面白い。

SHOW ROLES ->> SELECT *FROM $1 WHERE "name" = 'TF_DEMO_ROLE';


ユーザーに対して、ロールも紐づいてることが分かります。

SHOW grants to user TF_DEMO_USER;

権限
ロールに対する権限を見てみます。あれ?データベースとスキーマのusage権限しかないぞ!?とは驚かないでください。この時点ではテーブルが1つも存在しないからselect権限がないだけですね

SHOW grants to role TF_DEMO_ROLE;

テーブルを作成して権限が追加されるか見てみましょう。

use role SYSADMIN;
create or replace table TF_DEMO_DB.TF_DEMO_SC.TABLE_A(COL1 VARCHAR(10));
SHOW grants to role TF_DEMO_ROLE;

select権限が追加されましたね。期待値通りですね。

お掃除

Terraformでリソース作成を体験できたので、Terraformで管理されているリソースをすべて削除します

以下コマンドを実行すると作成した全てのリソースが削除されます。

terraform destroy

Snowsightから作成したリソースが削除されていること確認します。

最後にTerraform でリソース作成を始める前に作成したterrafrom実行用ユーザーを削除してQuickstartは終了です。
Snowsightで以下実行します。

DROP USER TERRAFORM_SVC;

次のステップ

次のステップは、Quickstartに記載されているのでそちらをご確認ください。
ステートファイルの管理方法、入力変数、モジュールの構築などなど学ぶことがたくさんありますし、terrafrom化できればCI/CD化もしたいですし、dbt等の他ツールとのすみ分けについても色々知りたいですよね!学ぶことはまだまだいっぱいあります。

おまけ

Terraformを運用していると新たらしいリソースを作成する際に、どのリソースをTerraformで管理するかは悩みの一つになってくると思います。

Terraform で全てのリソースを管理しないということは、以下スライドで紹介されていましたので見てみてください。
https://speakerdeck.com/pei0804/rebuilding-data-platform-with-snowflake?slide=40

Terraformのリソース作成方法が分かった所でここからTerragruntに入りたい所ですが、本記事ではここまでにして、その②でTerragruntはご紹介しようと思います。(リンクは本記事の「はじめに」にはりました)

以上、最後までお読みいただきありがとうございました!

ちゅらデータ株式会社

Discussion