SnowflakeからGlue Data Catalogを使ってIcebergテーブルにアクセスしてみる

# はじめに
最近よく聞く「Iceberg」に関する記事です！
Icebergテーブルとカタログの関係性など、少しスッキリしたので記事に残します🧊
では早速、SnowflakeからS3にあるIcebergテーブルへアクセスしてみます。

## 前提
- 外部カタログを使用
    - IcebergのカタログはAWSのGlue Data Catalogを
- この記事で紹介するAWSの作業は認証のみ
    - Glue Data CatalogにIcebergテーブルを作成済

以下のように
外部カタログ（Glue Data Catalog）を使用し、外部クラウドストレージ（AWSのS3）に保存されたテーブルデータとメタデータに、外部ボリュームを使用してアクセスします。
 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3586761/f9d837a1-323c-4309-8525-76848701e7bd.png)
[参考](https://docs.snowflake.com/ja/user-guide/tables-iceberg)

## 準備するもの
- **Snowflake**
    - カタログ統合
    - 外部ボリューム
    - Icebergテーブル
- **AWS**
    - IAMロール（SnowflakeリソースのIDを信頼関係に追加します）
    
# 手順
## 1 SnowflakeにAWS Glueのカタログ統合を作成
[参考：AWS Glueのカタログ統合を設定する](https://docs.snowflake.com/ja/user-guide/tables-iceberg-configure-catalog-integration-glue)
### 1-1 Snowflakeでカタログ統合を作成
以下のSQLを実行してAWS Glueのカタログ統合を作成します。
```カタログ統合.sql
--Glueのカタログ統合を作成
CREATE OR REPLACE CATALOG INTEGRATION <カタログ統合の名前>
  CATALOG_SOURCE = GLUE
  TABLE_FORMAT = ICEBERG
  GLUE_AWS_ROLE_ARN = '<IAMロールのARN>'
  GLUE_CATALOG_ID = '<Glue Data Catalog を管理する AWS アカウント ID>'
  GLUE_REGION = '<Glue Data Catalog の AWS リージョン>'
  CATALOG_NAMESPACE = '<Glue Data Catalog のデータベース名>'  
  ENABLED = TRUE -- カタログ統合をIcebergテーブルで使用する
;
```
### 1-2 SnowflakeアカウントのAWS IAMエンティティの情報と外部IDを取得
作成したカタログ統合を``DESCRIBE``して、AWSに渡す情報を取得します。
```DESCRIBE.sql
DESCRIBE CATALOG INTEGRATION <カタログ統合の名前>;
```
- (1) GLUE_AWS_IAM_USER_ARN
    - SnowflakeアカウントにあるAWS IAMエンティティ
- (2) GLUE_AWS_EXTERNAL_ID
    - 信頼関係を確立するために必要な外部ID

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3586761/220b1862-8f98-4c93-b43c-ee5b16799d9e.png)

### 1-3 IAMロールの信頼関係にSnowflakeの情報を追加
SnowflakeのAWS IAMエンティティにAWS側で用意したロールを引き渡すため、IAMロールの信頼関係にSnowflakeの情報を追加します。
これによりSnowflakeからAWSにアクセス出来ます。

```.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "<（1）>"
            },
            "Action": "sts:AssumeRole",
            "Condition": {
                "StringEquals": {
                    "sts:ExternalId": [
                        "< カタログ統合(2) >", 
                    ]
                }
            }
        }
    ]
}
```
## 2 外部ボリュームを作成

### 2-1 AWSに保存されているデータにアクセスするため外部ボリュームを作成
[Amazon S3用の外部ボリュームを構成する](https://docs.snowflake.com/ja/user-guide/tables-iceberg-configure-external-volume-s3)

以下を実行してSnowflakeに外部ボリュームを作成します。
```外部ボリューム作成.sql
CREATE OR REPLACE EXTERNAL VOLUME <外部ボリュームの名前>
   STORAGE_LOCATIONS =
      (
         (
            NAME = '<名前>'
            STORAGE_PROVIDER = 'S3'
            STORAGE_BASE_URL = 's3://<作成したs3バケット>'
            STORAGE_AWS_ROLE_ARN = '<IAMロールのARN>'
            STORAGE_AWS_EXTERNAL_ID = 'iceberg_table_external_id'
         )
      )
      ALLOW_WRITES = FALSE; --読み取り専用に
```

### 2-2 外部IDを取得
先ほどの1-2と同じように``DESCRIBE``して、AWSに渡す情報を取得します。
```DESCRIBE.sql
DESCRIBE EXTERNAL VOLUME <外部ボリュームの名前>;
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3586761/c0cb0605-8242-4d22-b5d0-a5ed56ab917e.png)

- (2) STORAGE_AWS_EXTERNAL_ID
    - 信頼関係を確立するために必要な外部ID

### 2-3 IAMロールの信頼関係にSnowflakeの情報を追加
1-3と同じようにIAMロールの信頼関係に追加します。

```.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "<（1）>"
            },
            "Action": "sts:AssumeRole",
            "Condition": {
                "StringEquals": {
                    "sts:ExternalId": [
                        "<カタログ統合(2)>",
                        "<外部ボリューム(2)>" 
                    ]
                }
            }
        }
    ]
}
```
### 2-4 ストレージにアクセスしてみる
AWSの認証が上手くいっているか確認のため、外部ボリュームにアクセスできるか試します。
```確認.sql
SELECT SYSTEM$VERIFY_EXTERNAL_VOLUME('<外部ボリューム名>');
```
成功すると``"success":true``が返却されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3586761/fa63f36f-4e4c-4eec-a76c-cfac7f763656.png)

外部ボリュームに読み取り専用 ``ALLOW_WRITES = FALSE;`` の設定を忘れると、
``s3:PutObject`` の権限が無いためエラーになります。(``"success":false``)

## 3 SnowflakeにIcebergテーブルを作成
### 3-1 IAMポリシーにS3の権限を追加
Snowflakeでテーブルを作成する前に、IAMポリシー に ``s3:GetObject``を追加します。これにより、外部ボリュームを使用してS3にあるデータへアクセス出来ます。
```IAMポリシー.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowGlueCatalogTableAccess", 
            "Effect": "Allow",
            "Action": [
                "glue:GetTable",
                "glue:GetTables"
            ],
            "Resource": [
                "arn:aws:glue:*:xxxxx:table/*/*",
                "arn:aws:glue:*:xxxxx:catalog",
                "arn:aws:glue:*:xxxxx:database/default"
            ]
        },
        {
            "Sid": "AllowS3BucketAccess", ★追加
            "Effect": "Allow",
            "Action": [
                "s3:GetObject"
            ],
            "Resource": "<S3のURL>/*"
        }
    ]
}
```

### 3-2 SnowflakeからGlue Data Catalog 上のテーブルを参照しIcebergテーブルを作成
以下のSQLを実行してIcebergテーブルを作成します。
```Icebergテーブル作成.sql
--データベース、スキーマ作成
USE DATABASE db;
USE SCHEMA sc;
--Icebergテーブル作成
CREATE ICEBERG TABLE db.sc.<テーブル名>
  EXTERNAL_VOLUME='<作成した外部ボリューム>' 
  CATALOG='<作成したカタログ統合>'
  CATALOG_TABLE_NAME='<Glue Data Catalogにあるテーブル名>';
```

### 3-3 IcebergテーブルをSELECTしデータが取得できるか確認
```確認.sql
SELECT * from db.sc.<テーブル名>;
```
Glue Data Catalogに作成したIcebergテーブルのデータがSELECTできました！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3586761/6e1e404e-3973-4c8d-bf36-8ae83bd098a3.png)

# おわりに
SnowflakeからIcebergテーブルへアクセスできるかの検証にあたり、カタログ統合/外部ボリューム/Icebergテーブルなど、新しい要素を一度に学べてお得でした。
はじめは何から手を付けるか分からずでしたが、以下を整理することが大事だなと感じました。
・どのカタログを使うの？（Snowflake管理？/外部のもの？）
・どこにあるデータを見る？（今回は外部のS3）
AWS認証周りの理解はストレージ統合の経験が役立ったなと思います。

AWSのGlue Data CatalogをSnowflakeでカタログ統合し外部ボリュームを作成することで、AWSにあるIcebergテーブルへアクセスできました。AWS Glue CatalogがもつIcebergテーブルの情報を、Snowflakeでも認識する為にカタログを統合していると理解しました。ご参考までに！🌵