はじめに
本記事では、Snowflakeのベストプラクティスに基づいたRBAC(ロールベースアクセス制御)を、Terraformで完全自動化する手順を解説します。特に「MFA(多要素認証)の連打地獄」から脱出するためのサービスユーザー導入と、開発者が安全に作業できる権限設計について、実際のトラブルシューティングも含めて紹介します。
背景:なぜサービスユーザーが必要なのか
直面した課題
Terraformでインフラをプロビジョニングする際、個人アカウントで認証すると以下の問題が発生しました:
- MFA(Duo Security)の連打: リソース作成のたびにスマホに承認通知が飛ぶ
- タイムアウトによるアカウントロック: 承認が遅れると一時的にロックされる
- 自動化の妨げ: CI/CDパイプラインでの利用が困難
Snowflakeのベストプラクティス
Snowflakeの公式ドキュメントでは、以下が推奨されています:
- 自動スクリプトにACCOUNTADMINを使用しない
- サービスユーザー(TYPE = SERVICE)の活用
- キーペア認証(JWT)による無人運用
アーキテクチャ設計
ロール階層の設計
Snowflakeのベストプラクティスに基づき、以下の階層構造を構築します:
[上位ロール:管理]
▲
│ (継承)
SYSADMIN(システム管理者:全ての「物」を把握する)
▲
│ (継承)
DEVELOPER_ROLE(機能ロール:開発者という「職種」)
▲
│ (継承)
DEV_FULL_ACCESS(アクセスロール:DB/Schemaへの「権限セット」)
▲
│ (権限付与)
[オブジェクト:DB, Schema, Stage, Table]
設計の原則:
- アクセスロール: 特定のオブジェクト(DB/Schema/Table)への権限を保持
- 機能ロール: ビジネス上の役割に対応し、複数のアクセスロールを束ねる
- SYSADMIN統合: すべてのカスタムロールを最終的にSYSADMINに付与し、管理を統合
ディレクトリ構成
infra/
├── main.tf
├── providers.tf
├── variables.tf
├── outputs.tf
├── terraform.tfvars # .gitignoreに追加必須
└── modules/
├── aws_s3_iam/ # AWS側のリソース
├── snowflake_base/ # DB/Warehouse/基本リソース
└── snowflake_iam/ # ユーザーとロール管理(今回新設)
├── main.tf
├── variables.tf
└── providers.tf
実装手順
Step 1: サービスユーザーの作成
1-1. RSAキーペアの生成
ローカル環境で以下のコマンドを実行します:
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
1-2. Snowflakeでサービスユーザーを作成
Snowflakeワークシートで以下のSQLを実行(ACCOUNTADMINロール):
USE ROLE ACCOUNTADMIN;
CREATE USER TERRAFORM_SVC
TYPE = SERVICE
COMMENT = "Service user for Terraforming Snowflake"
RSA_PUBLIC_KEY = 'ここにsnowflake_tf_snow_key.pubの中身を貼り付け';
-- 必要な権限を付与
GRANT ROLE SYSADMIN TO USER TERRAFORM_SVC;
GRANT ROLE SECURITYADMIN TO USER TERRAFORM_SVC;
GRANT ROLE USERADMIN TO USER TERRAFORM_SVC;
重要なポイント:
-
SYSADMIN: データベース、ウェアハウスなどのオブジェクト作成 -
SECURITYADMIN: 権限の付与・取り消し -
USERADMIN: ユーザーとロールの作成
Step 2: Terraformプロバイダーの設定
2-1. プロバイダー設定の更新
# infra/providers.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
snowflake = {
source = "snowflakedb/snowflake"
version = "~> 2.12.0"
}
}
}
provider "aws" {
region = var.aws_region
}
provider "snowflake" {
organization_name = var.snow_organization
account_name = var.snow_account
user = "TERRAFORM_SVC"
role = "SECURITYADMIN" # ユーザー/ロール作成のため
authenticator = "SNOWFLAKE_JWT"
private_key = file("~/.ssh/snowflake_tf_snow_key.p8")
}
2-2. 変数定義の整理
パスワード認証関連の変数を削除し、必要最小限に:
# infra/variables.tf
variable "snow_organization" {
type = string
description = "Snowflakeの組織名"
}
variable "snow_account" {
type = string
description = "Snowflakeのアカウント名"
}
variable "dev_user_password" {
type = string
sensitive = true
description = "開発者ユーザーの初期パスワード"
}
# その他の変数...
Step 3: RBAC モジュールの実装
3-1. ユーザーとロールの作成
# infra/modules/snowflake_iam/main.tf
# 1. 開発者ユーザーの作成
resource "snowflake_user" "developer_user" {
name = "DEV_USER"
login_name = "DEV_USER"
password = var.dev_user_password
default_role = "DEVELOPER_ROLE"
must_change_password = true # 初回ログイン時に変更を強制
}
# 2. アクセスロール(権限の箱)
resource "snowflake_account_role" "dev_access_role" {
name = "DEV_FULL_ACCESS"
}
# 3. 機能ロール(業務上の役割)
resource "snowflake_account_role" "developer_role" {
name = "DEVELOPER_ROLE"
}
3-2. 権限の付与
# データベースへのUSAGE権限
resource "snowflake_grant_privileges_to_account_role" "db_grant" {
privileges = ["USAGE"]
account_role_name = snowflake_account_role.dev_access_role.name
on_account_object {
object_type = "DATABASE"
object_name = var.database_name
}
}
# スキーマへの権限(開発に必要なもの)
resource "snowflake_grant_privileges_to_account_role" "schema_grant" {
privileges = ["USAGE", "CREATE TABLE", "CREATE STAGE", "CREATE PIPE"]
account_role_name = snowflake_account_role.dev_access_role.name
on_schema {
schema_name = "${var.database_name}.${var.schema_name}"
}
}
# ウェアハウスへのアクセス権
resource "snowflake_grant_privileges_to_account_role" "wh_grant" {
privileges = ["USAGE", "OPERATE"]
account_role_name = snowflake_account_role.dev_access_role.name
on_account_object {
object_type = "WAREHOUSE"
object_name = var.warehouse_name
}
}
3-3. ロール階層の構築
# アクセスロール → 機能ロール
resource "snowflake_grant_account_role" "access_to_functional" {
role_name = snowflake_account_role.dev_access_role.name
parent_role_name = snowflake_account_role.developer_role.name
}
# 機能ロール → SYSADMIN(管理の統合)
resource "snowflake_grant_account_role" "functional_to_sysadmin" {
role_name = snowflake_account_role.developer_role.name
parent_role_name = "SYSADMIN"
}
# 機能ロール → ユーザー
resource "snowflake_grant_account_role" "functional_to_user" {
role_name = snowflake_account_role.developer_role.name
user_name = snowflake_user.developer_user.name
}
3-4. 既存オブジェクトと将来のオブジェクトへの権限
開発者が既存のステージを参照でき、今後作成されるオブジェクトも自動で見えるように:
# 既存の全ステージへの権限
resource "snowflake_grant_privileges_to_account_role" "all_stage_grant" {
privileges = ["USAGE", "READ"]
account_role_name = snowflake_account_role.dev_access_role.name
on_schema_object {
all {
object_type_plural = "STAGES"
in_schema = "${var.database_name}.${var.schema_name}"
}
}
}
# 将来作成されるテーブルへの自動権限付与
resource "snowflake_grant_privileges_to_account_role" "future_table_grant" {
privileges = ["SELECT", "INSERT", "UPDATE", "DELETE"]
account_role_name = snowflake_account_role.dev_access_role.name
on_schema_object {
future {
object_type_plural = "TABLES"
in_schema = "${var.database_name}.${var.schema_name}"
}
}
}
Step 4: メインモジュールでの呼び出し
# infra/main.tf
module "snowflake_iam" {
source = "./modules/snowflake_iam"
database_name = "GOD_PROJECT_DB"
schema_name = "RAW"
warehouse_name = "GOD_PROJECT_WH"
dev_user_password = var.dev_user_password
}
トラブルシューティング
Issue 1: "password conflicts with private_key"
症状: キーペア認証に切り替えたのに、パスワード認証と競合するエラーが出る
原因: 環境変数にSNOWFLAKE_PASSWORDが残っている
解決策:
# Windows PowerShell
$env:SNOWFLAKE_PASSWORD=""
# Mac/Linux
unset SNOWFLAKE_PASSWORD
その後、Terraformを再初期化:
terraform init -upgrade
Issue 2: "Insufficient privileges to operate on account"
症状: サービスユーザーでユーザーやロールを作成しようとするとエラー
原因: SYSADMINロールはオブジェクト作成権限を持つが、ユーザー/ロール作成権限は持たない
解決策: サービスユーザーに適切なロールを付与
USE ROLE ACCOUNTADMIN;
GRANT ROLE USERADMIN TO USER TERRAFORM_SVC;
GRANT ROLE SECURITYADMIN TO USER TERRAFORM_SVC;
そして、プロバイダー設定で適切なロールを指定:
provider "snowflake" {
# ...
role = "SECURITYADMIN" # ユーザー/ロール作成とGRANT管理
}
Issue 3: "Stage does not exist or not authorized"
症状: 管理者が作成した既存のStageが、開発者ロールから見えない
原因: 既存オブジェクトへの権限が明示的に付与されていない
解決策: GRANT ... ON ALL STAGESを使用
resource "snowflake_grant_privileges_to_account_role" "all_stage_grant" {
privileges = ["USAGE", "READ"]
account_role_name = snowflake_account_role.dev_access_role.name
on_schema_object {
all {
object_type_plural = "STAGES"
in_schema = "${var.database_name}.${var.schema_name}"
}
}
}
Issue 4: カタログ(UI)にデータベースが表示されない
症状: SQLコマンドは成功するが、Snowsight UIでデータベースが見えない
原因: 以下画像のユーザー名の下にあるロールが間違っていた(画像の DEVELOPER_ROLE に表示されていた部分のロール権限のないロールだった)

解決策: 正しいロールを選択する
検証方法
1. ロール階層の確認
-- 作成したロールの確認
SHOW ROLES;
-- ロールの権限確認
SHOW GRANTS TO ROLE DEVELOPER_ROLE;
SHOW GRANTS ON ROLE DEVELOPER_ROLE;
2. 開発者ユーザーでのログイン
-
DEV_USERでSnowflakeにログイン - 初回ログイン時にパスワード変更を求められることを確認
- ロールを
DEVELOPER_ROLEに切り替え
3. 権限のテスト
USE ROLE DEVELOPER_ROLE;
USE WAREHOUSE GOD_PROJECT_WH;
-- テーブル作成権限の確認
CREATE TABLE GOD_PROJECT_DB.RAW.TEST_TABLE (ID INT);
-- 既存のステージへのアクセス確認
LS @GOD_PROJECT_DB.RAW.MY_S3_STAGE;
-- 自分で作成したステージの確認
CREATE STAGE GOD_PROJECT_DB.RAW.DEV_TEST_STAGE;
LS @GOD_PROJECT_DB.RAW.DEV_TEST_STAGE;
ベストプラクティス
セキュリティ
-
秘密鍵の管理
-
.p8ファイルは絶対にGitにコミットしない - 本番環境ではHashiCorp VaultやAWS Secrets Managerを使用
-
-
最小権限の原則
- 必要な権限のみを付与
- 本番環境では
AmazonS3FullAccessではなくカスタムポリシーを使用
-
MFAの適切な使用
- 人間用アカウントには必ずMFAを有効化
- サービスユーザーはキーペア認証のみ
運用
-
ロールの命名規則
- アクセスロール:
<対象>_<権限レベル>(例:DEV_FULL_ACCESS) - 機能ロール:
<業務役割>_ROLE(例:DEVELOPER_ROLE)
- アクセスロール:
-
将来の付与(Future Grants)の活用
- 新しく作成されるオブジェクトへの権限を自動化
- 権限管理の手間を大幅に削減
-
管理アクセススキーマの検討
- より厳密な権限管理が必要な場合は、Managed Access Schemaの使用を検討
コスト最適化
Warehouseの自動停止設定
resource "snowflake_warehouse" "this" {
name = "GOD_PROJECT_WH"
warehouse_size = "XSMALL"
auto_suspend = 60 # 60秒で自動停止
auto_resume = false # 勝手に起動させない
}
重要: auto_resume = trueは特に個人開発・ラボにおいて意図しないクエリで勝手に起動し、想定外の課金が発生します。そのため今回はauto_resume = falseとしましたが必要な時は明示的にウェアハウスをresumeする必要があり多少の手間はあります。
まとめ
本記事では、以下を実現しました:
- MFA地獄からの脱出: キーペア認証により、承認通知なしで高速デプロイ
- 適切なRBAC設計: ベストプラクティスに基づいた階層的な権限管理
- インフラ部分のTerraform化: ほとんど変化しないインフラのコード化により再現性を確保
- 開発者体験の向上: 適切な権限により、ストレスなく開発・テストが可能
次のステップ
この基盤の上に、以下を構築してみたいと考えています:
- Snowpipe: S3からの自動データ取り込み
- RDS連携: 上流DB役のRDSからのデータ連携
- dbt統合: データ変換の自動化
- Terraformの適切な分割: インフラ、ユーザー作成、権限設定を分割して影響範囲を狭める&それぞれの領域に必要な権限を付与
