[新機能]Snowflake から AWS Secrets Manager などのシークレットを直接参照できるようになりました
はじめに
2026年9月のアップデートで、クラウド上のシークレット管理サービスに保存した値を Snowflake から直接読み取れる外部シークレットプロバイダー(External Secret Providers)がパブリックプレビューとなりました。
AWS Secrets Manager について、こちらの機能を試してみた内容を本記事でまとめます。
アップデートの概要
本機能については以下に記載があります。
外部シークレットプロバイダーは、クラウド上のシークレット管理サービスを Snowflake から読み取れるようにする機能です。執筆時点では、以下のプロバイダーがサポートされています。
- AWS Secrets Manager
- Azure Key Vault
- Google Cloud Secret Manager
これまで Snowflake でシークレットを保存する際は、値そのものをSECRETオブジェクトとして保存・参照する、というのが一般的な方法でした。この方法と比較すると、以下のような特徴があります。
- 他システムでも同じシークレットを使っている場合、クラウド側だけ更新すればよく、Snowflake 側への反映を忘れて値が食い違う、といったことが起きない
- クラウド側でシークレットをローテーションしていれば、Snowflake は毎回最新値を直接参照するため、Snowflake 側での手動更新が不要
- クラウド側の監査ログ・IAM ポリシーで管理を一本化できる
また、認証には Workload Identity Federation が使われ、クラウド側のアクセスキーやサービスアカウントキーを Snowflake 側に保存する必要がない仕様となっています。
本機能では、クラウド側との接続をセキュリティ統合オブジェクトとして作成します。Snowflake 側では、この統合オブジェクトに対してUSAGE権限を持つロールがシークレットにアクセスできる仕組みになっています。
Snowflake 側で実際にシークレットを扱うために、以下の2つのシステム関数が提供されています。
- SYSTEM$LIST_EXTERNAL_SECRETS
- 統合を通じて到達可能なシークレットの一覧(AWS の場合は ARN)を取得する
- SYSTEM$FETCH_EXTERNAL_SECRET_FROM_INTEGRATION
- 指定したシークレットの値を取得する
試してみる
本記事では、AWS Secrets Manager に保存した dbt platform の API キーを Snowflake から参照し、Snowflake からジョブをキックする、という構成で試してみます。
AWS Secrets Manager との連携手順は以下に記載があるので、こちらに沿って進めます。
前提条件
以下の環境を使用しています。
- Snowflake:商用アカウント
- 外部ネットワークアクセスのため
- dbt platform
シークレットオブジェクトを使用する方法でも検証記事があるので、こちらもあわせてご参照ください。
事前準備
検証に使用する各種オブジェクトの作成先を以下の内容で作成しました。
CREATE DATABASE IF NOT EXISTS yasuhara_test_db;
CREATE SCHEMA IF NOT EXISTS yasuhara_test_db.network_rule;
CREATE SCHEMA IF NOT EXISTS yasuhara_test_db.procedure;
CREATE SCHEMA IF NOT EXISTS yasuhara_test_db.task;
AWS Secrets Manager にシークレットを登録
dbt platform の「Account settings > Service tokens」よりサービストークンを発行します。今回はジョブ実行のみを試すので、対象のプロジェクトの「Job Runner」権限セットを付与しておきました。

発行した AWS Secrets Manager に登録します。
aws secretsmanager create-secret \
--name dbt-cloud-service-token \
--description "dbt Cloud service token for Snowflake External Secret Provider" \
--secret-string "<dbt サービストークン>"
出力:
{
"ARN": "arn:aws:secretsmanager:<region>:<account_id>:secret:dbt-cloud-service-token-xxxxxx",
"Name": "dbt-cloud-service-token",
"VersionId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
出力のARNは、この後の IAM ロールと Snowflake からのシークレット取得の両方で使うため控えておきます。
Snowflake 側:セキュリティ統合を作成
AWS 側の IAM ロールはまだ作成せず、ここでは名前だけ仮置きして先にセキュリティ統合を作成します。
USE ROLE ACCOUNTADMIN;
CREATE SECURITY INTEGRATION yasuhara_aws_sm_integration
TYPE = API_AUTHENTICATION
AUTH_TYPE = WORKLOAD_IDENTITY_FEDERATION
API_PROVIDER = AWS_SECRETS_MANAGER
AWS_ROLE_ARN = 'arn:aws:iam::<account_id>:role/yasuhara-snowflake-ext-secret-role'
AWS_REGION = '<region>'
ENABLED = TRUE;
作成後、以下のコマンドで発行者(WORKLOAD_IDENTITY_FEDERATION_ISSUER)とサブジェクト(WORKLOAD_IDENTITY_FEDERATION_SUBJECT)を取得します。この値を使って、次の手順で AWS 側に OIDC ID プロバイダーと IAM ロールを作成します。
DESCRIBE SECURITY INTEGRATION yasuhara_aws_sm_integration;
AWS 側:OIDC ID プロバイダーと IAM ロールを作成
はじめに OIDC ID プロバイダーを作成します。
aws iam create-open-id-connect-provider \
--url "<WORKLOAD_IDENTITY_FEDERATION_ISSUERの値>" \
--client-id-list "sts.amazonaws.com"
続けて信頼ポリシーを作成します。:sub 条件を入れることで、この特定のセキュリティ統合だけに信頼を限定できます。Federated と Condition 内の <issuer> には、いずれも先ほど取得した WORKLOAD_IDENTITY_FEDERATION_ISSUER の値(https:// を除いたホスト以降の部分)を指定します。
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::<account_id>:oidc-provider/<WORKLOAD_IDENTITY_FEDERATION_ISSUERの値(https://を除く)>"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"<WORKLOAD_IDENTITY_FEDERATION_ISSUERの値(https://を除く)>:aud": "sts.amazonaws.com",
"<WORKLOAD_IDENTITY_FEDERATION_ISSUERの値(https://を除く)>:sub": "<WORKLOAD_IDENTITY_FEDERATION_SUBJECTの値>"
}
}
}]
}
このポリシーを使って IAM ロールを作成し、シークレットへのアクセス権限を付与します。
aws iam create-role \
--role-name yasuhara-snowflake-ext-secret-role \
--assume-role-policy-document file://trust-policy.json
aws iam put-role-policy \
--role-name yasuhara-snowflake-ext-secret-role \
--policy-name yasuhara-snowflake-ext-secret-access \
--policy-document file://secret-access-policy.json
secret-access-policy.jsonでは、対象シークレットへの secretsmanager:GetSecretValueと、全リソースに対するsecretsmanager:ListSecretsを許可しています。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "secretsmanager:GetSecretValue",
"Resource": "<登録したシークレットのARN>"
},
{
"Effect": "Allow",
"Action": "secretsmanager:ListSecrets",
"Resource": "*"
}
]
}
Snowflake 側:連携を確認
上述の設定後、Snowflake 側で以下のコマンドを実行します。以下のような出力が返ってくれば問題ありません。
SELECT SYSTEM$VERIFY_EXTERNAL_SECRET_INTEGRATION('yasuhara_aws_sm_integration');
+--------------------------------------------------------------------------+
| SYSTEM$VERIFY_EXTERNAL_SECRET_INTEGRATION('YASUHARA_AWS_SM_INTEGRATION') |
|--------------------------------------------------------------------------|
| Verification successful. |
+--------------------------------------------------------------------------+
あわせて、利用するロールに USAGE 権限を付与しておきます。
GRANT USAGE ON INTEGRATION yasuhara_aws_sm_integration TO ROLE <role>;
この状態で、システム関数によるシークレットの一覧・値の取得も試してみます。
-- 統合オブジェクトを通じて到達可能なシークレットの一覧(ARN)を取得
SELECT SYSTEM$LIST_EXTERNAL_SECRETS('yasuhara_aws_sm_integration');
-- 指定したシークレットの値を取得
SELECT PARSE_JSON(
SYSTEM$FETCH_EXTERNAL_SECRET_FROM_INTEGRATION(
'yasuhara_aws_sm_integration',
'<secret_arn>')):value::STRING;
シークレットの値は平文で返るため、実行結果をログや画面共有・スクリーンショットに残さないよう注意が必要です。

Snowpark Python ストアドプロシージャから dbt platform API を呼び出す
まず、dbt platform の API(cloud.getdbt.com)への通信を許可するネットワークルールと外部アクセス統合を作成します。
-- ネットワークルール
CREATE OR REPLACE NETWORK RULE yasuhara_test_db.network_rule.yasuhara_dbt_cloud_network_rule
MODE = EGRESS
TYPE = HOST_PORT
VALUE_LIST = ('cloud.getdbt.com');
-- 外部アクセス統合
CREATE OR REPLACE EXTERNAL ACCESS INTEGRATION yasuhara_dbt_cloud_access_integration
ALLOWED_NETWORK_RULES = (yasuhara_test_db.network_rule.yasuhara_dbt_cloud_network_rule)
ENABLED = TRUE;
続けて、dbt platform の API を呼び出すストアドプロシージャを定義します。
この際、はじめはストアドプロシージャ内でSYSTEM$FETCH_EXTERNAL_SECRET_FROM_INTEGRATIONを実行しようと思ったのですが、以下のエラーになりました。
SQL compilation error:
Query called from a stored procedure contains a function with side effects [SYSTEM$FETCH_EXTERNAL_SECRET_FROM_INTEGRATION].
そのため、ここではシークレットの取得は呼び出し元のCALL文で行い、取得済みの値をプロシージャへ引数として渡す形としています。
USE SCHEMA yasuhara_test_db.procedure;
CREATE OR REPLACE PROCEDURE yasuhara_trigger_dbt_cloud_job(job_id NUMBER, dbt_token STRING)
RETURNS VARIANT
LANGUAGE PYTHON
RUNTIME_VERSION = '3.11'
HANDLER = 'trigger_job'
EXTERNAL_ACCESS_INTEGRATIONS = (yasuhara_dbt_cloud_access_integration)
PACKAGES = ('snowflake-snowpark-python', 'requests')
AS
$$
import requests
DBT_CLOUD_ACCOUNT_ID = "<dbt platformのAccount ID>"
def trigger_job(job_id: int, dbt_token: str) -> dict:
url = f"https://cloud.getdbt.com/api/v2/accounts/{DBT_CLOUD_ACCOUNT_ID}/jobs/{job_id}/run/"
resp = requests.post(
url,
headers={"Authorization": f"Token {dbt_token}"},
json={"cause": "Triggered from Snowflake procedure (External Secret Provider)"},
timeout=30,
)
return {"status_code": resp.status_code, "body": resp.json()}
$$;
ストアドプロシージャ実行時は、SQL でシークレットを取得しつつ、引数として渡します。
CALL yasuhara_test_db.procedure.yasuhara_trigger_dbt_cloud_job(
'<job_id>',
PARSE_JSON(SYSTEM$FETCH_EXTERNAL_SECRET_FROM_INTEGRATION('yasuhara_aws_sm_integration', '<secret_arn>')):value::STRING
);
実行すると、dbt platform 側でジョブが正常にキックされました。

Task で実行する
このプロシージャをタスクに組み込み、スケジュール実行する場合は以下のように定義できます。
CREATE OR REPLACE TASK yasuhara_test_db.task.yasuhara_trigger_dbt_cloud_job_task
WAREHOUSE = <warehouse_name>
SCHEDULE = 'USING CRON 0 */12 * * * Asia/Tokyo'
AS
CALL yasuhara_test_db.procedure.yasuhara_trigger_dbt_cloud_job(
'<job_id>',
PARSE_JSON(SYSTEM$FETCH_EXTERNAL_SECRET_FROM_INTEGRATION('yasuhara_aws_sm_integration', '<secret_arn>')):value::STRING
);
試しに手動実行すると、同じようにジョブがキックされていました。
EXECUTE TASK yasuhara_test_db.task.yasuhara_trigger_dbt_cloud_job_task;
SYSTEM$FETCH_EXTERNAL_SECRET_FROM_INTEGRATIONとクエリ履歴
なお、シークレットの取得にはSYSTEM$FETCH_EXTERNAL_SECRET_FROM_INTEGRATIONを使用しますが、このシステム関数には「呼び出したクエリ自体がクエリ履歴に残らない」という仕様があります。
シークレットをローテーションしてみる
最後に、AWS 側でシークレットの値を更新しても追随できるか確認します。
dbt platform で同じ権限セットを持つトークンを発行後、トークンを更新します。
aws secretsmanager put-secret-value \
--secret-id dbt-cloud-service-token \
--secret-string "<新しいdbt サービストークン>"
SYSTEM$FETCH_EXTERNAL_SECRET_FROM_INTEGRATIONは常に最新版を読み込む仕様のため、Snowflake 側のオブジェクトは特に変更しなくても、直後の取得から新しい値が返ってきます。
その後、再度プロシージャを実行しましたが問題なくジョブを実行できました。

さいごに
Snowflake の外部シークレットプロバイダーを、AWS Secrets Manager で試してみました。シークレット管理を一元化しつつ、Snowflake 側の変更は不要で常に最新のシークレットを取得できる点が特徴と感じました。
こちらの内容がどなたかの参考になれば幸いです。




