AWS 上の dbt Core から Snowflake へ接続するに、JWT を利用した Workload Identity を利用する
こんにちは、川田です。
今回は、AWS 上の dbt Core から Snowflake へ接続するに、JWT ベースの Workload Identity を利用できるかどうか確認しました。
環境
- dbt Core: 1.12.2
- adapter (dbt-snowflake): 1.12.0
結論
以下の方法にて利用が可能でした。
まず、Snowflake Connector で JWT ベースの接続をする場合、以下パラメーター(★印3つ)を指定することになります。
conn = snowflake.connector.connect(
account='<snowflake_account>',
authenticator='WORKLOAD_IDENTITY', # ★
workload_identity_provider='AWS', # ★
workload_identity_aws_use_outbound_token=True # ★
)
dbt から接続しようとした場合、本確認環境のバージョンでは、 workload_identity_aws_use_outbound_token パラメーターが dbt profiles.yml でサポートされておらず、OS 環境変数での設定が必要となりました。
dbt profiles.yml の設定。★印部分が、Workload Identity 向けのパラメーター。
my_snowflake_project:
target: prod
outputs:
prod:
type: snowflake
account: "<snowflake_account>"
user: "TEST_DBT_CONNECT_JWT"
role: "SYSADMIN"
database: "HOGEHOGE_DB"
warehouse: "HOGEHOGE_WH"
schema: "PUBLIC"
authenticator: WORKLOAD_IDENTITY # ★
workload_identity_provider: AWS # ★
併せて、OS 環境変数に SNOWFLAKE_ENABLE_AWS_WIF_OUTBOUND_TOKEN の設定が必要。
export SNOWFLAKE_ENABLE_AWS_WIF_OUTBOUND_TOKEN=true
以降より、動作確認した結果を記載しています。
前提情報
Snowflake Workload Identity を利用して、AWS IAM Role により Snowflake と接続するには、以下の 2 つの方法があります。
AWS WIF supports two approaches: the default
GetCallerIdentity-basedapproach and a JWT-based approach usingGetWebIdentityToken. Snowflake drivers support both. Snowflake recommends the JWT-based method for a more future-proof approach that aligns with industry-standard token patterns.
(訳) AWS WIF では、デフォルトの
GetCallerIdentityベースのアプローチと、GetWebIdentityTokenを使用する JWT ベースのアプローチの 2 つのアプローチがサポートされています。Snowflake ドライバーは、これら両方をサポートしています。Snowflake では、業界標準のトークンパターンに準拠し、将来性のあるアプローチとして、JWT ベースの方法を推奨しています。
Authenticate using AWS IAM roles and a Snowflake Python driver | Snowflake
今回確認した内容は、Snowflake 推奨の GetWebIdentityToken を使用する JWT ベースのアプローチを dbt でも利用できるか確認したものです。
動作検証
今回は EC2 上に dbt Core をインストールし、Snowflake への接続に利用する IAM Role を、その EC2 に付与する方針で検証しています。
アウトバウンド ID フェデレーション有効化
AWS アカウントでアウトバウンド ID フェデレーションを有効にします。
aws iam enable-outbound-web-identity-federation
アウトバウンド ID フェデレーションの開始方法 | AWS
IAM Role 作成
test-dbt-connect-to-snowflake-using-jwt という名前の IAM Role を作成しています。
# EC2 に付与するための trust-policy
cat > trust-policy.json <<'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": { "Service": "ec2.amazonaws.com" },
"Action": "sts:AssumeRole"
}
]
}
EOF
aws iam create-role \
--role-name test-dbt-connect-to-snowflake-using-jwt \
--assume-role-policy-document file://trust-policy.json
GetWebIdentityToken を呼び出すための IAM ポリシーを付与します。audience claim には snowflakecomputing.com を、署名アルゴリズムとして ES384 を指定します。
cat > wif-outbound-policy.json <<'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sts:GetWebIdentityToken",
"Resource": "*",
"Condition": {
"ForAllValues:StringEquals": {
"sts:IdentityTokenAudience": "snowflakecomputing.com"
},
"NumericLessThanEquals": {
"sts:DurationSeconds": 300
},
"StringEquals": {
"sts:SigningAlgorithm": "ES384"
}
}
}
]
}
EOF
aws iam put-role-policy \
--role-name test-dbt-connect-to-snowflake-using-jwt \
--policy-name AllowGetWebIdentityToken \
--policy-document file://wif-outbound-policy.json
- 参考
GetWebIdentityTokenで採用できる署名アルゴリズム- Snowflake 側でサポートされている署名アルゴリズム
Snowflake ユーザー作成
上記作成の IAM Role と紐づく Snowflake ユーザーを作成します。
USE ROLE USERADMIN;
CREATE USER TEST_DBT_CONNECT_JWT
WORKLOAD_IDENTITY = (
TYPE = AWS
ARN = 'arn:aws:iam::012345678901:role/test-dbt-connect-to-snowflake-using-jwt' -- 利用する AWS IAM Role を指定
ISSUER = 'https://00000000-0000-0000-0000-000000000000.tokens.sts.global.api.aws' -- 作成したアウトバウンド ID フェデレーションより取得した値
)
TYPE = SERVICE
DEFAULT_ROLE = SYSADMIN
;
USE ROLE SYSADMIN;
GRANT ROLE SYSADMIN TO USER TEST_DBT_CONNECT_JWT;
dbt profile.yml 作成
EC2 上の dbt 環境で ~/.dbt/profiles.yml を作成します。
my_snowflake_project:
target: prod
outputs:
prod:
type: snowflake
account: "<snowflake_account>"
user: "TEST_DBT_CONNECT_JWT"
role: "SYSADMIN"
database: "HOGEHOGE_DB"
warehouse: "HOGEHOGE_WH"
schema: "PUBLIC"
authenticator: WORKLOAD_IDENTITY # ★
workload_identity_provider: AWS # ★
接続確認
まず、SNOWFLAKE_ENABLE_AWS_WIF_OUTBOUND_TOKEN を環境変数で設定します。
export SNOWFLAKE_ENABLE_AWS_WIF_OUTBOUND_TOKEN=true
dbt debug にて接続確認します。
$ $ dbt debug
05:03:15 Running with dbt=1.12.2
05:03:15 dbt version: 1.12.2
05:03:15 python version: 3.11.15
05:03:15 python path: /home/ec2-user/dbt-venv/bin/python3.11
05:03:15 os info: Linux-6.1.177-224.371.amzn2023.x86_64-x86_64-with-glibc2.34
05:03:16 Using profiles dir at /home/ec2-user/.dbt
05:03:16 Using profiles.yml file at /home/ec2-user/.dbt/profiles.yml
05:03:16 Using dbt_project.yml file at /home/ec2-user/dbt_project/my_snowflake_project/dbt_project.yml
05:03:16 adapter type: snowflake
05:03:16 adapter version: 1.12.0
05:03:16 Configuration:
05:03:16 profiles.yml file [OK found and valid]
05:03:16 dbt_project.yml file [OK found and valid]
05:03:16 Required dependencies:
05:03:16 - git [ERROR]
05:03:16 Connection:
05:03:16 account: snowflake_account
05:03:16 user: TEST_DBT_CONNECT_JWT
05:03:16 database: HOGEHOGE_DB
05:03:16 warehouse: HOGEHOGE_WH
05:03:16 role: SYSADMIN
05:03:16 schema: PUBLIC
05:03:16 authenticator: WORKLOAD_IDENTITY
05:03:16 oauth_client_id: None
05:03:16 query_tag: None
05:03:16 client_session_keep_alive: False
05:03:16 host: None
05:03:16 port: None
05:03:16 proxy_host: None
05:03:16 proxy_port: None
05:03:16 protocol: None
05:03:16 connect_retries: 1
05:03:16 connect_timeout: None
05:03:16 retry_on_database_errors: False
05:03:16 retry_all: False
05:03:16 insecure_mode: False
05:03:16 reuse_connections: True
05:03:16 s3_stage_vpce_dns_name: None
05:03:16 workload_identity_provider: AWS
05:03:16 workload_identity_entra_resource: None
05:03:16 platform_detection_timeout_seconds: 0.0
05:03:16 Registered adapter: snowflake=1.12.0
05:03:17 Connection test: [OK connection ok]
05:03:17 1 check failed:
05:03:17 Error from git --help: Could not find command, ensure it is in the user's PATH: "git"
接続できています。なお、git に関するメッセージは、EC2 上に git が不在のため発生しているもので、本確認とは関係ないものです。
確認
CloudTrail のイベントより、GetWebIdentityToken が呼ばれていることを確認します。
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=EventName,AttributeValue=GetWebIdentityToken \
--start-time "$(date -u -d '10 minutes ago' +%Y-%m-%dT%H:%M:%SZ)" \
| jq -r '.Events[].CloudTrailEvent' | jq .
{
"eventVersion": "1.11",
"userIdentity": {
"type": "AssumedRole",
"principalId": "AAAAAAAAAAAAAAAAAAA:i-00000000000000000",
"arn": "arn:aws:sts::012345678901:assumed-role/test-dbt-connect-to-snowflake-using-jwt/i-00000000000000000",
"accountId": "012345678901",
"accessKeyId": "AAAAAAAAAAAAAAAAA",
"sessionContext": {
"sessionIssuer": {
"type": "Role",
"principalId": "AAAAAAAAAAAAAAAAAAA",
"arn": "arn:aws:iam::012345678901:role/test-dbt-connect-to-snowflake-using-jwt",
"accountId": "012345678901",
"userName": "test-dbt-connect-to-snowflake-using-jwt"
},
"attributes": {
"creationDate": "2026-08-14T04:53:56Z",
"mfaAuthenticated": "false"
},
"ec2RoleDelivery": "2.0"
},
"inScopeOf": {
"issuerType": "AWS::EC2::Instance",
"credentialsIssuedTo": "arn:aws:ec2:ap-northeast-1:012345678901:instance/i-00000000000000000"
}
},
"eventTime": "2026-08-14T05:03:16Z",
"eventSource": "sts.amazonaws.com",
"eventName": "GetWebIdentityToken",
"awsRegion": "ap-northeast-1",
"sourceIPAddress": "203.0.113.1",
"userAgent": "Boto3/1.43.71 md/Botocore#1.43.71 ua/2.1 os/linux#6.1.177-224.371.amzn2023.x86_64 md/arch#x86_64 lang/python#3.11.15 md/pyimpl#CPython m/Z,b,D cfg/retry-mode#legacy Botocore/1.43.71",
"requestParameters": {
"audience": [
"snowflakecomputing.com"
],
"signingAlgorithm": "ES384"
},
"responseElements": {
"webIdentityTokenId": "00000000-0000-0000-0000-000000000000",
"expiration": "2026-08-14T05:08:16Z"
},
"additionalEventData": {
"ExtendedRequestId": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"RequestDetails": {
"endpointType": "regional",
"awsServingRegion": "ap-northeast-1"
}
},
"requestID": "dd11bc80-ace6-4bc6-9d96-23933e090a57",
"eventID": "37c1454f-8dc6-4514-b7d7-96b7a0ed7763",
"readOnly": false,
"eventType": "AwsApiCall",
"managementEvent": true,
"recipientAccountId": "012345678901",
"eventCategory": "Management",
"tlsDetails": {
"tlsVersion": "TLSv1.3",
"cipherSuite": "TLS_AES_128_GCM_SHA256",
"clientProvidedHostHeader": "sts.ap-northeast-1.amazonaws.com",
"keyExchange": "secp256r1"
}
}







