Workload Identity Federation for Amazon S3 Storage integrations

This topic describes how to configure a Snowflake storage integration for Amazon S3 using Workload Identity Federation (WIF). With WIF, Snowflake uses an OpenID Connect (OIDC) trust relationship instead of a shared IAM user and external ID, so there are no long-lived credentials to manage.

Prerequisites

  • Access to an AWS account with permissions to create IAM identity providers and roles.
  • An Amazon S3 bucket that you want Snowflake to access.
  • A Snowflake account with a role that has the CREATE INTEGRATION privilege (for example, ACCOUNTADMIN).

Step 1: Retrieve Snowflake’s OIDC issuer URL

Retrieve Snowflake’s OIDC issuer URL before configuring anything on the AWS side. You set this URL as the issuer when you create the AWS identity provider in the next step.

In Snowflake, run:

SELECT SYSTEM$GET_WORKLOAD_IDENTITY_ISSUER_URL();

Copy the output. You use it in the next step.

Step 2: Create an identity provider in AWS

Use the output from Step 1 to set up a new identity provider in AWS.

  1. In your AWS console, go to IAM > Identity providers > Add provider.
  2. Select Web Identity.
  3. For Provider URL, enter the URL from Step 1.
  4. For Audience, enter audience of choice (e.g.sts.amazonaws.com)
  5. Select Add provider.

Step 3: Create the IAM role in AWS

To configure access permissions for Snowflake in the AWS Management Console, do the following:

  1. From the left-hand navigation pane in the Identity and Access Management (IAM) Dashboard, select Roles.

  2. Select Create role.

  3. Select Web identity and, from the drop-down, find the provider and audience you configured in Step 2.

  4. Add permissions for access (use an existing policy or an inline policy).

    An example inline policy that grants the role access to your bucket might look like the following:

    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Sid": "Statement1",
                "Effect": "Allow",
                "Action": [
                    "s3:PutObject",
                    "s3:GetObject",
                    "s3:GetObjectVersion",
                    "s3:DeleteObject",
                    "s3:DeleteObjectVersion"
                ],
                "Resource": "arn:aws:s3:::bucket/path/to/files/*"
            },
            {
                "Effect": "Allow",
                "Action": [
                    "s3:ListBucket",
                    "s3:GetBucketLocation"
                ],
                "Resource": "arn:aws:s3:::bucket",
                "Condition": {
                    "StringLike": {
                        "s3:prefix": [
                            "path/to/files/*"
                        ]
                    }
                }
            }
        ]
    }
    
  5. On the role summary page, locate and record the Role ARN value. In the next step, you create a Snowflake integration that references this role.

Step 4: Create the storage integration in Snowflake

Create a storage integration that references the IAM role ARN from the previous step:

CREATE OR REPLACE STORAGE INTEGRATION s3_int
    TYPE = EXTERNAL_STAGE
    STORAGE_PROVIDER = 'S3'
    STORAGE_ALLOWED_LOCATIONS = ('s3://bucket/path/to/files/')
    STORAGE_AWS_ROLE_ARN = 'arn:aws:iam::XXXXXXX:role/my-role'
    AUTH_TYPE = WORKLOAD_IDENTITY_FEDERATION
    ENABLED = TRUE;

Step 5: Verify the integration

Verify the integration by running DESC INTEGRATION:

DESC INTEGRATION s3_int;

Look for these rows in the output:

propertyproperty_value
AUTH_TYPEWORKLOAD_IDENTITY_FEDERATION
STORAGE_AWS_ROLE_ARNarn:aws:iam::XXXXXXX:role/my-role
WORKLOAD_IDENTITY_FEDERATION_ISSUERhttps://<auto-generated URL>
WORKLOAD_IDENTITY_FEDERATION_SUBJECTidentity_external_stage_integration_<hex>

Key things to note:

  • Issuer and subject are system-generated: you didn’t provide them. Snowflake creates a unique cryptographic identity for this integration automatically.
  • No STORAGE_AWS_IAM_USER_ARN: there’s no shared IAM user. WIF replaces the traditional trust model entirely.
  • No STORAGE_AWS_EXTERNAL_ID: OIDC trust replaces the external ID mechanism.

Step 6: Create an external stage

CREATE OR REPLACE STAGE my_s3_stage
    STORAGE_INTEGRATION = s3_int
    URL = 's3://bucket/path/to/files/';

Verify the stage works:

LIST @my_s3_stage;

The LIST command should return an empty result (no files yet) without errors.

Note

If you get an access denied error, wait 1–2 minutes and retry. AWS OIDC provider propagation can take up to 60 seconds.

Step 7: Upload and query data (Optional)

This step is an optional walkthrough that verifies end-to-end access by unloading sample data to the stage and loading it back.

Upload a file (unload)

-- Create a small table to export
CREATE OR REPLACE TABLE sales_source (id INT, name STRING);
INSERT INTO sales_source VALUES (1, 'Alice'), (2, 'Bob'), (3, 'Charlie');

-- Unload to the stage (COPY INTO @stage)
COPY INTO @my_s3_stage/sales_data_
    FROM sales_source
    FILE_FORMAT = (TYPE = CSV)
    OVERWRITE = TRUE;

Verify the file exists:

LIST @my_s3_stage;

You should see one or more CSV files listed.

Load data back from the stage

CREATE OR REPLACE TABLE sales_loaded (id INT, name STRING);

COPY INTO sales_loaded
    FROM @my_s3_stage/
    FILE_FORMAT = (TYPE = CSV);

SELECT * FROM sales_loaded;

You should see the 3 rows (Alice, Bob, Charlie) loaded back successfully.

Limitations and considerations

During Private Preview, the following limitations apply:

  • Cross-cloud and cross-partition configurations aren’t supported.
  • Reads from a replicated storage integration on the replicated secondary aren’t supported. Reads work after the secondary becomes the primary.
  • Sovereign and federated clouds aren’t supported.