Snowflake

Snowflake Storage Integrations: Secure Authentication for External Stages

2026-03-26
更新: 2026-03-27
NicheeLab Editorial Team

A Storage Integration is a Snowflake object that centrally manages authentication when Snowflake accesses external cloud storage (S3, Azure Blob, or GCS). Instead of embedding access keys and secrets directly in stage definitions, you configure access delegation to an IAM role or service principal on the cloud provider side. This removes the need to distribute and rotate long-lived keys.

How Storage Integrations Work

When you create a Storage Integration, Snowflake internally provisions a service account specific to the cloud provider (an IAM user ARN in the case of AWS). You then configure a Trust Policy on the cloud side that allows this service account to assume the role, enabling Snowflake to access the storage securely.

ConceptDescription
Storage IntegrationAccount-level authentication object in Snowflake
STORAGE_ALLOWED_LOCATIONSList of cloud storage paths permitted for access
STORAGE_BLOCKED_LOCATIONSList of paths denied even within allowed locations
External StageStage definition that references a Storage Integration
DESCRIBE INTEGRATIONCommand to retrieve the IAM ARN and External ID generated by Snowflake

Authentication Flow (AWS S3)

┌──────────────────────┐
│  Snowflake           │
│  Storage Integration │
│  (STORAGE_AWS_IAM_   │
│   USER_ARN を保持)   │
└────────┬─────────────┘
         │ 1. Assume Role リクエスト
         │    (External ID を付与)
         ▼
┌──────────────────────┐
│  AWS IAM Role        │
│  (Trust Policy で    │
│   Snowflake ARN +    │
│   External ID を許可)│
└────────┬─────────────┘
         │ 2. 一時クレデンシャル発行
         ▼
┌──────────────────────┐
│  S3 Bucket           │
│  /orders/            │
│  (IAM Roleの権限で   │
│   読み取り/書き込み)  │
└──────────────────────┘

CREATE STORAGE INTEGRATION (AWS S3)

-- AWS S3用 Storage Integration
CREATE OR REPLACE STORAGE INTEGRATION s3_orders_int
  TYPE = EXTERNAL_STAGE
  STORAGE_PROVIDER = 'S3'
  ENABLED = TRUE
  STORAGE_AWS_ROLE_ARN = 'arn:aws:iam::123456789012:role/snowflake-access-role'
  STORAGE_ALLOWED_LOCATIONS = (
    's3://company-data-lake/orders/',
    's3://company-data-lake/returns/'
  )
  STORAGE_BLOCKED_LOCATIONS = (
    's3://company-data-lake/orders/secrets/'
  );

CREATE STORAGE INTEGRATION (Azure Blob)

-- Azure Blob Storage用 Storage Integration
CREATE OR REPLACE STORAGE INTEGRATION azure_data_int
  TYPE = EXTERNAL_STAGE
  STORAGE_PROVIDER = 'AZURE'
  ENABLED = TRUE
  AZURE_TENANT_ID = 'a]1b2c3d4-e5f6-7890-abcd-ef1234567890'
  STORAGE_ALLOWED_LOCATIONS = (
    'azure://myaccount.blob.core.windows.net/data-container/input/'
  );

CREATE STORAGE INTEGRATION (GCS)

-- Google Cloud Storage用 Storage Integration
CREATE OR REPLACE STORAGE INTEGRATION gcs_analytics_int
  TYPE = EXTERNAL_STAGE
  STORAGE_PROVIDER = 'GCS'
  ENABLED = TRUE
  STORAGE_ALLOWED_LOCATIONS = (
    'gcs://analytics-bucket/raw/',
    'gcs://analytics-bucket/processed/'
  );

DESCRIBE INTEGRATION Output

After creating a Storage Integration, run DESCRIBE INTEGRATION to retrieve the service account information that Snowflake generated. You then plug these values into the Trust Policy on the cloud side.

DESC INTEGRATION s3_orders_int;

-- 主要な出力項目(AWSの場合)
-- ┌─────────────────────────────┬──────────────────────────────────────┐
-- │ property                    │ property_value                       │
-- ├─────────────────────────────┼──────────────────────────────────────┤
-- │ STORAGE_AWS_IAM_USER_ARN    │ arn:aws:iam::987654321098:user/abc123│
-- │ STORAGE_AWS_EXTERNAL_ID     │ ABC123_SFCRole=2_dGVzdA==            │
-- │ STORAGE_ALLOWED_LOCATIONS   │ s3://company-data-lake/orders/,...   │
-- │ STORAGE_BLOCKED_LOCATIONS   │ s3://company-data-lake/orders/sec... │
-- └─────────────────────────────┴──────────────────────────────────────┘

IAM Trust Policy Configuration (AWS)

Take the STORAGE_AWS_IAM_USER_ARN and STORAGE_AWS_EXTERNAL_ID returned by DESCRIBE INTEGRATION and apply them to the Trust Policy of the IAM role on the AWS side.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::987654321098:user/abc123"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": {
          "sts:ExternalId": "ABC123_SFCRole=2_dGVzdA=="
        }
      }
    }
  ]
}

Grant the IAM role permissions for GetObject / ListBucket (read access) on the S3 bucket, and add PutObject / DeleteObject (write access) if needed.

Creating an External Stage

-- Storage Integrationを参照するExternal Stage
CREATE OR REPLACE STAGE ext_orders_stage
  URL = 's3://company-data-lake/orders/'
  STORAGE_INTEGRATION = s3_orders_int
  FILE_FORMAT = (TYPE = 'PARQUET');

-- ステージ上のファイル確認
LIST @ext_orders_stage;

-- データロード
COPY INTO ORDERS
  FROM @ext_orders_stage
  PATTERN = '.*\.parquet'
  ON_ERROR = 'CONTINUE';

-- データアンロード
COPY INTO @ext_orders_stage/export/
  FROM ORDERS
  FILE_FORMAT = (TYPE = 'PARQUET')
  HEADER = TRUE;

Direct Authentication vs Storage Integration

AspectDirect authentication (embedded keys)Storage Integration
SecurityLow (plain-text keys exposed in stage definition)High (IAM role delegation, no keys)
Key managementManual rotation requiredTemporary credentials issued automatically
Key rotationRequires recreating the stageNot required (handled on the cloud side)
Access scopeDepends on the key's permissionsControlled via ALLOWED/BLOCKED_LOCATIONS
Sharing across stagesKeys configured per stageOne Integration shared across multiple stages
RecommendationNot recommended (legacy)Recommended (official best practice)

Privilege Model

OperationRequired privilegeTarget role
CREATE INTEGRATIONCREATE INTEGRATION (account level)ACCOUNTADMIN or a delegated role
ALTER / DROP INTEGRATIONOWNERSHIP ON INTEGRATIONRole that owns the Integration
Reference from a stageUSAGE ON INTEGRATIONRole that creates the stage
DESCRIBE INTEGRATIONUSAGE ON INTEGRATIONAnyone reviewing configuration
-- 権限付与の例
-- ACCOUNTADMINがIntegrationを作成
USE ROLE ACCOUNTADMIN;
CREATE STORAGE INTEGRATION s3_orders_int ...;

-- データエンジニアロールにUSAGE権限を付与
GRANT USAGE ON INTEGRATION s3_orders_int TO ROLE DATA_ENGINEER;

-- DATA_ENGINEERがステージを作成
USE ROLE DATA_ENGINEER;
CREATE STAGE ext_orders
  URL = 's3://company-data-lake/orders/'
  STORAGE_INTEGRATION = s3_orders_int;

Troubleshooting

Error / SymptomCauseResolution
Failure using stage area. Cause: Access DeniedSnowflake's ARN is not configured in the IAM role's Trust PolicySet STORAGE_AWS_IAM_USER_ARN from DESC INTEGRATION in the Trust Policy
External ID mismatchThe ExternalId in the Trust Policy doesn't match the DESC outputCopy STORAGE_AWS_EXTERNAL_ID exactly and reconfigure
Insufficient privileges on integrationThe stage-creating role lacks USAGE privilegeGRANT USAGE ON INTEGRATION ... TO ROLE ...
ListBucket Access DeniedIAM policy is missing s3:ListBucketAdd ListBucket to the IAM policy (Resource: bucket ARN)
KMS Access DeniedInsufficient KMS permissions on an SSE-KMS encrypted bucketAdd kms:Decrypt and kms:GenerateDataKey to the IAM policy
Files in the specified path are invisibleThe stage URL doesn't match STORAGE_ALLOWED_LOCATIONSVerify the URL is within the prefix range of ALLOWED_LOCATIONS

Sample Question

Architect / Data Engineer

問題 1

A team created an External Stage to load Parquet files from S3 into Snowflake. The CREATE STAGE statement specifies the STORAGE_INTEGRATION parameter, but running LIST @stage returns an 'Access Denied' error. DESCRIBE on the Storage Integration returns normally. What is the most likely cause?

  1. The AWS IAM role's Trust Policy is missing the STORAGE_AWS_IAM_USER_ARN and STORAGE_AWS_EXTERNAL_ID from DESCRIBE INTEGRATION
  2. The stage's FILE_FORMAT is set to CSV so Parquet can't be read
  3. The warehouse is not running so the stage cannot be accessed
  4. STORAGE_BLOCKED_LOCATIONS specifies the entire S3 bucket

正解: A

Since DESCRIBE INTEGRATION returns successfully, the Snowflake-side Integration configuration itself is correct. An Access Denied error points to a permissions issue on the AWS side, and the most common cause is that the IAM role's Trust Policy doesn't correctly reference Snowflake's service account (STORAGE_AWS_IAM_USER_ARN) and ExternalId (STORAGE_AWS_EXTERNAL_ID). Option B (FILE_FORMAT) would only surface during data parsing, not on a LIST call. Option C (warehouse): a warehouse is needed for LIST, but the 'Access Denied' message indicates a permissions issue, not a warehouse problem. Option D (BLOCKED_LOCATIONS): specifying the entire bucket in BLOCKED_LOCATIONS would error at Integration creation time, so it wouldn't manifest here.

Frequently Asked Questions

Can I create an External Stage without a Storage Integration?

Technically, you can create an External Stage by specifying AWS_KEY_ID and AWS_SECRET_KEY directly. However, the credentials are embedded in the stage definition in plain text, which can be exposed to other roles via SHOW STAGES — a significant security risk. The official Snowflake documentation strongly recommends Storage Integrations, which use IAM role-based access delegation and remove the need to manage long-lived keys. Certification exams also frame Storage Integration as the 'more secure option.'

How do STORAGE_ALLOWED_LOCATIONS and STORAGE_BLOCKED_LOCATIONS interact?

STORAGE_ALLOWED_LOCATIONS is an explicit list of cloud storage paths that the Storage Integration is permitted to access. STORAGE_BLOCKED_LOCATIONS is an exclusion list that denies access to specific sub-paths even if they fall within an allowed location. For example, you can permit all of s3://data-bucket/ while excluding s3://data-bucket/secrets/. Combining the two lets you enforce fine-grained access control aligned with the principle of least privilege.

What is the best practice for using multiple Storage Integrations?

The recommended pattern is to separate Storage Integrations by environment (prod/staging/dev). Each Integration's STORAGE_ALLOWED_LOCATIONS should permit only the bucket paths for that environment, and USAGE ON INTEGRATION should be granted to environment-specific roles. This eliminates the risk of developers accidentally touching production buckets. From an audit perspective, it also makes it easier to track access logs per Integration through ACCOUNT_USAGE.ACCESS_HISTORY.

Check what you learned with practice questions

Practice with certification-focused question sets

無料で問題を解いてみる
Author

NicheeLab Editorial Team

NicheeLab editorial team focused on data engineering and cloud certification learning. Content is structured around practical study needs and official exam domains.


Related articles
Snowflake

Snowflake Certifications: All 11 Exams Explained (2026)

Every SnowPro certification — Associate, Core, Specialty, Ad...

Snowflake

Snowflake Exam Difficulty Ranking: All 11 Certs Compared (2026)

All 11 SnowPro exams ranked by difficulty with study-time es...

Snowflake

Snowflake Study Guide: Fastest Pass Route by Exam (2026)

How to pass SnowPro certifications efficiently — official ma...

Snowflake

SnowPro Core (COF-C03): Complete Exam Guide (2026)

Pass the SnowPro Core exam — six domains, scope, sample ques...

Snowflake

SnowPro Associate Platform (SOL-C01): Complete Guide (2026)

The entry-level SnowPro Associate exam — scope, weighting, s...

Browse all Snowflake articles (103)
© 2026 NicheeLab All rights reserved.