Snowflakeとlambdaを接続してみる

# はじめに
この記事は、[NTTテクノクロス Advent Calendar 2024（シリーズ 2）](https://qiita.com/advent-calendar/2024/ntt-tx) 13日目の記事です。

NTTテクノクロスの吉田と申します。

本記事では、AWSのLambdaとSnowflakeを試しに接続する際の手順の一例を記載させていただきます。

# Snowflakeについて
Snowflakeとは、クラウド上で動くデータウェアハウスです。いくつか特徴を上げていきます。
- クラウド管理かつブラウザ上で操作するため、ハードウェアやソフトウェアの準備が不要である
- 負荷対策でのスケールアップが容易である
- 従量課金モデルのため、稼働していない時間は費用を抑えることができる
- AWSなどの各種プラットフォームとの連携のサポートドキュメントが充実している

その他の詳細については公式ドキュメントをご確認ください
- [Snowflakeドキュメント](https://docs.snowflake.com/ja)

# AWSのlambdaとの連携
今回はLambdaで作成した関数を[外部関数](https://docs.snowflake.com/ja/sql-reference/external-functions-introduction)としてSnowflakeから呼び出してその結果を受けとるための手順を作成します。
これにより、複雑な処理が必要となった際に、Snowflake内のクエリだけでなくLambdaのjavaを使用することができるようになります。

# 手順
## [AWS]Lambdaの作成
AWSの [Lambda > 関数] で「関数の作成」を押下して新規関数を作成する。
- 例
    - 関数名：Connect_Snowflake
    - ランタイム：Python 3.12

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3584180/f704d58d-87f4-2f3a-47db-1e5608152627.png)

タブ「コード」に[サンプル同期Lambda関数](https://docs.snowflake.com/ja/sql-reference/external-functions-creating-aws-sample-synchronous)のソースコードを張り付ける
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3584180/673749ea-904f-76de-5810-3474ea7daef4.png)


タブ「テスト」に「[Lambda関数をテストする](https://docs.snowflake.com/ja/sql-reference/external-functions-creating-aws-ui-remote-service#test-the-lambda-function)」のサンプルを「イベント JSON」に貼り付けてテストする。
- イベントアクションをテスト：新しいイベントを作成
- イベント名：hello-world

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3584180/9059d526-de67-83ab-a26a-0628d885921f.png)

実行結果の例
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3584180/436aa022-e557-60ed-6495-fd52c996eab3.png)

## [AWS]Lambdaの作成
AWSの [IAM > ロール] で「ロールを作成」を押下して新規ロールを作成する。
- 信頼されたエンティティタイプ：AWSアカウント
- AWSアカウント：Lambdaを作成した際に使用したアカウントを選択
![スクリーンショット 2024-12-10 153828.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3584180/1deaa23d-9d03-4248-c1d0-300f8bc44924.png)

- ロール名：任意の名前

## [AWS]APIの作成
AWSの [API Gateway > API] でREST APIの構築を押下して、APIを作成する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3584180/520a7d83-8e94-1ff7-901d-75dd4909022b.png)
- APIの詳細：新しいAPI
- API名：TestAPI
- APIエンドポイントタイプ：リージョン
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3584180/08883ff4-4947-ddd5-1aee-5db326e41182.png)

AWSの [API Gateway > API > 先ほど作成したAPI名]でメソッドを作成を押下して、メソッドを作成する
- メソッドタイプ：POST
- 統合タイプ：Lambda 関数
- Lambdaプロキシ統合：on
- Lambda関数：作成したLambda関数を設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3584180/fd579f9d-c903-4cf3-c107-ba1ba5f212e5.png)

APIのデプロイを行う
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3584180/9691dccc-930b-2d95-27c9-aa7fa2fa466d.png)

- ステージ名：任意のステージ名
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3584180/ab39c53c-9a06-43d9-ea80-e66b00e4bf53.png)
作成後に表示されるURLは保存しておいてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3584180/f509c3c8-a869-0ffc-66bb-1a383d3c873b.png)

[Amazon API Gatewayエンドポイントの保護](https://docs.snowflake.com/ja/sql-reference/external-functions-creating-aws-ui-proxy-service#secure-your-amazon-api-gateway-endpoint)にあるリソースのエンドポイントのソースをコピーして、新規作成するリソースポリシーに貼り付けてください。その後、以下の3つを置換してください。
- 12-digit-number：AWSのアカウントID（12桁）
- external_function_role：作成したIAMロール名
- method_request_ARN：メソッドリクエストのARN（API Gateway > API > 作成したリソース で作成したメソッドを押下すると表示される）
※：以下の画像は置換前の例です
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3584180/4554e2f6-72b8-f918-3630-cade28479bea.png)

AWSの「API Gateway > API > 作成したリソース」で作成したメソッドを押下し、メソッドリクエストの設定を変更する
- 認可：AWS_IAM
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3584180/6fa4f6cf-22c5-5d7e-61ac-b2ed2ee613b5.png)


## [Snowflake]API統合の作成
Snowflakeの [プロジェクト > ワークシート] で新しいワークシートを作成し、対象のデータベースを選択後に以下のSQLを投入してAPI統合を作成する。
- api_aws_role_arn：作成したロールのARN
- api_allowed_prefixes：「API Gateway > API > 作成したAPI > ステージ」の「URL を呼び出す」
```
create or replace api integration test_integration
  api_provider = aws_api_gateway
  api_aws_role_arn = '<api_aws_role_arn>'
  enabled = true
  api_allowed_prefixes = ('<APIの呼び出しURL>')
;
```
登録状況を確認する。実行結果から以下の2つのパラメータをメモする
- API_AWS_IAM_USER_ARN
- API_AWS_EXTERNAL_ID
```
describe integration test_integration;
```

## [AWS]Snowflakeの情報を追加する

AWSの「IAM > ロール > 作成したロール」の信頼関係タグで信頼ポリシーの編集を行う
以下の個所を編集すること
- AWS：Snowflakeから取得したAPI_AWS_IAM_USER_ARN
- Condition：Snowflakeから取得したAPI_AWS_EXTERNAL_ID
    - 以下の通り記載して★の個所を置換すること
    - "Condition": {"StringEquals": { "sts:ExternalId": "★" }}

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3584180/c98ea99a-de2c-6273-8b9e-a8bcebb04e6d.png)

## [Snowflake]外部関数の作成

以下のクエリを作成して投入し、外部関数を作成する。
- api_integration_name：先ほど作成した統合APIの名前
- invocation_url：APIの呼び出しURL（api_allowed_prefixesに設定したURLと同じ）
```
create external function test_external_function(i integer, c varchar)
    returns variant
    api_integration = <api_integration_name>
    as '<invocation_url>'
```

## [Snowflake]外部関数の実行
以下のクエリを作成して投入することで、外部関数が実行され、lambda関数の処理が実行されます。
結果として投入した2つの引数をlambdaで一つの文字列として出力してSnowflakeに返しています
```
SELECT test_external_function(1234567890, 'テスト文字列') as "実行結果";
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3584180/93066dcb-912c-e218-06e3-0e2f81ac7511.png)

# 最後に
以上でLambdaで作成した関数をSnowflakeから呼び出してその結果を受けとる作業は終了となります。
Snowflakeを使用した実例があまりないため、本手順が参考になれば幸いです。