SnowflakeのS3とのストレージ統合をCloudFormationでさくっと設定する

SnowflakeからAmazon S3に接続する場合の推奨方法であるストレージ統合(Storage Integration)のための AWS側の設定をAWS CloudFormationテンプレートを使って構築する。
2021.07.01

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

データアナリティクス事業本部、池田です。
別のブログ作業でSnowflakeAmazon S3 を接続する必要があり、 推奨される ストレージ統合(Storage Integration) で行いました。その作業を AWS CloudFormation でできるようにテンプレートを作成したので、簡単に紹介します。

↓以下の記事を参考にしてテンプレートを作成しました。
Amazon S3からSnowflakeへのバルクロードを設定からやってみた
Simplifying Snowflake access to S3

Snowflake側作業

先にストレージ統合をSQLで作ってしまいます。
ACCOUNTADMIN ロールなどで作業する必要があります。

USE ROLE ACCOUNTADMIN;

CREATE STORAGE INTEGRATION {ストレージ統合名}
    TYPE = EXTERNAL_STAGE
    STORAGE_PROVIDER = S3
    ENABLED = TRUE
    STORAGE_AWS_ROLE_ARN = 'arn:aws:iam::{S3のAWSアカウントID}:role/{作成予定のロール名}'
    STORAGE_ALLOWED_LOCATIONS = ('s3://{S3のバケット名}/{パス}/');

DESC INTEGRATION {ストレージ統合名};

(次章では、作成予定のロール名のデフォルトを snowflake-s3-access-role としています。)
成功すると↑の最終行のDESCの結果として、
↓以下のような情報が得られます。

property	property_type	property_value	property_default
ENABLED	Boolean	true	false
STORAGE_PROVIDER	String	S3	
STORAGE_ALLOWED_LOCATIONS	List	s3://{S3のバケット名}/{パス}/	[]
STORAGE_BLOCKED_LOCATIONS	List		[]
STORAGE_AWS_IAM_USER_ARN	String	{SnowflakeのIAMユーザー(CloudFormationで使う)}	
STORAGE_AWS_ROLE_ARN	String	arn:aws:iam::{S3のAWSアカウントID}:role/{作成予定のロール名}	
STORAGE_AWS_EXTERNAL_ID	String	{外部ID(CloudFormationで使う)}

STORAGE_AWS_IAM_USER_ARNSTORAGE_AWS_EXTERNAL_ID次章で必要になります。

AWS側作業

以下ようなCloudFormationテンプレートを作成しました。 これを使ってCloudFormationのコンソールや CLI などからリソースの作成ができます。

AWSTemplateFormatVersion: "2010-09-09"
Description: "Create a role for Snowflake."

Parameters:
  RoleName:
    Type: "String"
    Default: "snowflake-s3-access-role"
  BucketName:
    Type: "String"
  BucketPrefix:
    Type: "String"
  StorageAwsIamUserArn:
    Type: "String"
    Description: "DESC INTEGRATION {integration_name};"
  StorageAwsExternalId:
    Type: "String"
    Description: "DESC INTEGRATION {integration_name};"

Resources:
  SnowflakeS3AccessRole:
    Type: "AWS::IAM::Role"
    Properties:
      RoleName: !Ref RoleName
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              AWS: !Ref StorageAwsIamUserArn
            Action: sts:AssumeRole
            Condition:
              StringLike:
                sts:ExternalId: !Ref StorageAwsExternalId
      Policies:
        - PolicyName: !Sub "${RoleName}-policy"
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - s3:GetObject
                  - s3:GetObjectVersion
                  # - s3:PutObject
                  # - s3:DeleteObject
                Resource: !Sub "arn:aws:s3:::${BucketName}/${BucketPrefix}/*"
              - Effect: Allow
                Action:
                  - s3:ListBucket
                Resource: !Sub "arn:aws:s3:::${BucketName}"
                Condition:
                  StringLike:
                    s3:prefix: !Sub "${BucketPrefix}/*"

(ポリシーはロードのための参照系に絞っています。)
コンソールで作成した場合のパラメータは↓こんな感じ。

(作成予定のロール名→ snowflake-s3-access-role 、S3のパス(ディレクトリ)→ snowflake としています。)

動作確認

↓こんな感じで簡単な外部ステージを作成して、ファイルが参照できるか確認しました。

USE ROLE ACCOUNTADMIN;

CREATE STAGE CHECK_EXT_STAGE_S3
    STORAGE_INTEGRATION = {ストレージ統合名}
    URL = 's3://{S3のバケット名}/{パス}/';

list @CHECK_EXT_STAGE_S3;


実際はこのあと、 GRANT USAGE ON INTEGRATION {ストレージ統合名} TO ROLE {実際に使用するロール}; で、ACCOUNTADMIN以外でも作成したストレージ統合を使用できるように権限を振って、 きちんとしたステージを作成しました。

おわりに

今回は簡単な紹介のため、 接続するS3のパスが複数の場合や、 KMS の考慮はしていません。 必要に応じて冒頭で紹介した記事など参考に適宜テンプレート変更して使っていただけたらと思います。

関連情報/参考にさせていただいたページ