[New Feature] You can now directly reference secrets from AWS Secrets Manager and other sources from Snowflake

[New Feature] You can now directly reference secrets from AWS Secrets Manager and other sources from Snowflake

I tried out Snowflake's external secret provider feature in conjunction with AWS Secrets Manager. Here I will summarize the actual configuration steps, from setting up the security integration to making API calls on the dbt platform.
2026.09.14

This page has been translated by machine translation. View original

Introduction

In the September 2026 update, External Secret Providers — which allow Snowflake to directly read values stored in cloud secret management services — entered public preview.

https://docs.snowflake.com/en/release-notes/2026/other/2026-09-10-external-secret-providers-preview

This article summarizes what I tried with this feature using AWS Secrets Manager.

Overview of the Update

This feature is described in the following documentation.

https://docs.snowflake.com/en/user-guide/external-secret-providers

External Secret Providers is a feature that allows Snowflake to read from cloud secret management services. At the time of writing, the following providers are supported.

  • AWS Secrets Manager
  • Azure Key Vault
  • Google Cloud Secret Manager

Previously, the common approach for storing secrets in Snowflake was to store and reference the values themselves as SECRET objects. Compared to that approach, this feature has the following characteristics.

  • If the same secret is used in other systems, you only need to update it on the cloud side, eliminating the risk of discrepancies caused by forgetting to reflect changes on the Snowflake side
  • If secrets are being rotated on the cloud side, Snowflake always references the latest value directly, so manual updates on the Snowflake side are unnecessary
  • Management can be consolidated through cloud-side audit logs and IAM policies

Additionally, Workload Identity Federation is used for authentication, meaning there is no need to store cloud-side access keys or service account keys on the Snowflake side.

With this feature, the connection to the cloud side is created as a security integration object. On the Snowflake side, roles with USAGE privileges on this integration object can access secrets.

Two system functions are provided for actually handling secrets on the Snowflake side.

Trying It Out

In this article, I'll try a configuration where an API key for dbt platform stored in AWS Secrets Manager is referenced from Snowflake, and a job is triggered from Snowflake.
The integration procedure with AWS Secrets Manager is described below, so I'll follow along with it.

https://docs.snowflake.com/en/user-guide/external-secret-providers-aws

Prerequisites

The following environments are used.

  • Snowflake: Commercial account
    • For external network access
  • dbt platform

There is also a verification article using the secret object approach, so please refer to that as well.

https://dev.classmethod.jp/articles/snowflake-dbt-job-triggered-by-task/

Preparation

I created the various objects used for verification with the following setup.

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;

Registering a Secret in AWS Secrets Manager

Issue a service token from dbt platform under "Account settings > Service tokens." Since I'm only testing job execution this time, I granted the "Job Runner" permission set for the target project.

2026-09-14_10h00_36

https://docs.getdbt.com/docs/dbt-apis/service-tokens

Register the issued token in 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 service token>"

Output:

{
    "ARN": "arn:aws:secretsmanager:<region>:<account_id>:secret:dbt-cloud-service-token-xxxxxx",
    "Name": "dbt-cloud-service-token",
    "VersionId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}

Keep note of the ARN in the output, as it will be used both for the IAM role and for retrieving secrets from Snowflake later.

Snowflake Side: Creating a Security Integration

Without creating the IAM role on the AWS side yet, we'll create the security integration first with just a placeholder name.

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;

After creation, run the following command to retrieve the issuer (WORKLOAD_IDENTITY_FEDERATION_ISSUER) and subject (WORKLOAD_IDENTITY_FEDERATION_SUBJECT). These values will be used in the next step to create an OIDC identity provider and IAM role on the AWS side.

DESCRIBE SECURITY INTEGRATION yasuhara_aws_sm_integration;

AWS Side: Creating an OIDC Identity Provider and IAM Role

First, create the OIDC identity provider.

aws iam create-open-id-connect-provider \
  --url "<value of WORKLOAD_IDENTITY_FEDERATION_ISSUER>" \
  --client-id-list "sts.amazonaws.com"

Next, create a trust policy. By including the :sub condition, trust can be limited to this specific security integration only. For both Federated and <issuer> in Condition, specify the value of WORKLOAD_IDENTITY_FEDERATION_ISSUER retrieved earlier (the part after the host, excluding https://).

trust-policy.json
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": {
      "Federated": "arn:aws:iam::<account_id>:oidc-provider/<value of WORKLOAD_IDENTITY_FEDERATION_ISSUER (excluding https://)>"
    },
    "Action": "sts:AssumeRoleWithWebIdentity",
    "Condition": {
      "StringEquals": {
        "<value of WORKLOAD_IDENTITY_FEDERATION_ISSUER (excluding https://)>:aud": "sts.amazonaws.com",
        "<value of WORKLOAD_IDENTITY_FEDERATION_ISSUER (excluding https://)>:sub": "<value of WORKLOAD_IDENTITY_FEDERATION_SUBJECT>"
      }
    }
  }]
}

Use this policy to create an IAM role and grant access permissions to the secret.

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

In secret-access-policy.json, secretsmanager:GetSecretValue for the target secret and secretsmanager:ListSecrets for all resources are permitted.

secret-access-policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "secretsmanager:GetSecretValue",
      "Resource": "<ARN of the registered secret>"
    },
    {
      "Effect": "Allow",
      "Action": "secretsmanager:ListSecrets",
      "Resource": "*"
    }
  ]
}

Snowflake Side: Verifying the Integration

After the above configuration, run the following command on the Snowflake side. If the output shown below is returned, there are no issues.

SELECT SYSTEM$VERIFY_EXTERNAL_SECRET_INTEGRATION('yasuhara_aws_sm_integration');
+--------------------------------------------------------------------------+                                                                                         
| SYSTEM$VERIFY_EXTERNAL_SECRET_INTEGRATION('YASUHARA_AWS_SM_INTEGRATION') |                                                                                         
|--------------------------------------------------------------------------|                                                                                         
| Verification successful.                                                 |                                                                                         
+--------------------------------------------------------------------------+ 

Also, grant USAGE privileges to the role you will use.

GRANT USAGE ON INTEGRATION yasuhara_aws_sm_integration TO ROLE <role>;

In this state, let's also try listing secrets and retrieving values using the system functions.

-- Retrieve a list (ARNs) of secrets reachable through the integration object
SELECT SYSTEM$LIST_EXTERNAL_SECRETS('yasuhara_aws_sm_integration');

-- Retrieve the value of a specified secret
SELECT PARSE_JSON(
  SYSTEM$FETCH_EXTERNAL_SECRET_FROM_INTEGRATION(
    'yasuhara_aws_sm_integration',
    '<secret_arn>')):value::STRING;

Since secret values are returned in plain text, be careful not to leave execution results in logs, screen shares, or screenshots.

2026-09-14_21h42_37

Calling the dbt Platform API from a Snowpark Python Stored Procedure

First, create a network rule and external access integration to allow communication to the dbt platform API (cloud.getdbt.com).

-- Network rule
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');

-- External access integration
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;

Next, define a stored procedure that calls the dbt platform API.
At first, I tried running SYSTEM$FETCH_EXTERNAL_SECRET_FROM_INTEGRATION inside the stored procedure, but I got the following error.

SQL compilation error:
Query called from a stored procedure contains a function with side effects [SYSTEM$FETCH_EXTERNAL_SECRET_FROM_INTEGRATION].

Therefore, the secret retrieval is done in the calling CALL statement, and the retrieved value is passed as an argument to the procedure.

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()}
$$;

https://docs.getdbt.com/dbt-cloud/api-v2?version=2#/operations/Trigger Job Run

When executing the stored procedure, retrieve the secret using SQL and pass it as an argument.

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
);

Upon execution, the job was successfully triggered on the dbt platform side.

2026-09-14_16h35_58

Running with a Task

To incorporate this procedure into a task and run it on a schedule, it can be defined as follows.

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
  );

When I ran it manually as a test, the job was triggered in the same way.

EXECUTE TASK yasuhara_test_db.task.yasuhara_trigger_dbt_cloud_job_task;

SYSTEM$FETCH_EXTERNAL_SECRET_FROM_INTEGRATION and Query History

Note that SYSTEM$FETCH_EXTERNAL_SECRET_FROM_INTEGRATION is used to retrieve secrets, and this system function has a specification where "the query that called it does not remain in the query history."

Rotating the Secret

Finally, let's verify that updates to the secret value on the AWS side are reflected automatically.

After issuing a new token with the same permission set on dbt platform, update the token.

aws secretsmanager put-secret-value \
  --secret-id dbt-cloud-service-token \
  --secret-string "<new dbt service token>"

Since SYSTEM$FETCH_EXTERNAL_SECRET_FROM_INTEGRATION is designed to always read the latest version, the new value will be returned from the very next retrieval without any changes needed on the Snowflake side.

After that, I ran the procedure again and was able to execute the job without any issues.

2026-09-14_22h20_25

Conclusion

I tried out Snowflake's External Secret Providers with AWS Secrets Manager. The standout feature is the ability to centralize secret management while always retrieving the latest secrets without any changes needed on the Snowflake side.
I hope this content is helpful to someone.


Snowflake Community Awards ファイナリストに選出されました

DevelopersIO で Snowflake 記事を執筆している かわばた が、Snowflake Community Awards「RISING COMMUNITY LEADER OF THE YEAR」部門・APJ枠のファイナリストに選ばれました。
最終選考の30%はコミュニティ投票です。記事がお役に立っていたようでしたら、9月15日(火)までにぜひ一票お願いします。フォームの「(4 of 6) RISING COMMUNITY LEADER OF THE YEAR」で Tomohiro Kawabata | Classmethod, Japan を選択、2分ほどで完了します。

投票フォームを開く


Snowflakeの導入支援はクラスメソッドに!

クラスメソッドでは Snowflake の導入を支援しております。
製品の詳細や支援の内容についてお気軽にお問い合わせください。

Snowflakeの詳細を見る

Share this article