AWS での外部関数の作成

このドキュメントでは、Amazon AWS で外部関数を作成する1つの方法を示しています。

  • リモートサービスの作成( AWS のLambda関数)

  • AWS API Gatewayでのプロキシサービスの作成。

  • Snowflakeでの API統合の作成。

  • Snowflakeでの外部関数の作成。

  • 外部関数の呼び出し。

これらの手順では、Amazon AWS の管理に精通していることを前提としています。これらの手順では、実行する必要のある一般的なステップを指定しますが、 AWS 管理コンソールの詳細については、詳細が変更される可能性があるため説明しません。

このトピックの内容:

前提条件

必須:

  • 次の権限を含む、 AWS のアカウント。

    • IAM を介した AWS ロールの作成(IDおよびアクセス管理)。

    • Lambda関数の作成。

    • API Gatewayエンドポイントの作成。

  • ACCOUNTADMIN 権限または CREATE INTEGRATION 権限があるロールを持っているSnowflakeアカウント。

このドキュメントは、経験豊富な AWS 管理者を前提としています。

ちなみに

外部関数の作成中に、認証関連の情報をSnowflakeからクラウドプラットフォームにコピーし、クラウドプラットフォームの管理ウィンドウからSnowflakeにコピーする必要があります。

以下の手順を実行する際に、いくつかのステップで情報を記録して、後のステップで使用することをお勧めします。

Cloud Platform (IAM) Account Id: _____________________________________________
Lambda Function Name...........: _____________________________________________
New IAM Role Name..............: _____________________________________________
Cloud Platform (IAM) Role ARN..: _____________________________________________
Proxy Service Resource Name....: _____________________________________________
Resource Invocation URL........: _____________________________________________
Method Request ARN.............: _____________________________________________
API_AWS_IAM_USER_ARN...........: _____________________________________________
API_AWS_EXTERNAL_ID............: _____________________________________________

ステップ1:リモートサービスを作成する( AWS のLambda関数)

リモートサービスを作成するには、複数の方法があります。このセクションでは、 AWS Lambdaで実行されるPython関数として実装されるリモートサービスを作成する方法を示します。

このサンプルPython言語関数は、単にその入力を返します。

この関数は、Snowflakeが送信するのと同じ形式のデータを受け入れ、Snowflakeが読み取るのと同じ形式のデータを返します。

このPython関数は、 eventcontext の2つのパラメーターを受け取ります。 event パラメーターには多くのサブフィールドが含まれ、そのうちの1つは body です。本文は data という名前のキーを含む辞書です。 data の対応する値は、Snowflakeが JSON 形式で送信したデータを保持する文字列です。AWS Lambdaは、Snowflakeから送信された HTTP POST リクエストを便利に処理し、本文を抽出してイベントパラメーターの本文を渡すため、このサンプル関数は HTTP POST リクエスト全体を解析する必要がありません。

このPython関数が本文を文字列として抽出した後、関数は JSON ライブラリ関数を呼び出して文字列をPythonデータ構造に変換します。次に、関数はそのデータ構造から個々の行を抽出し、それらを処理して、それぞれの値を返します。

AWS Lambda関数からの一般的な戻り値の JSON は次のようになります。

{
"statusCode": <http_status_code>,
"body":
        {
            "data":
                  [
                      [ 0, <value> ],
                      [ 1, <value> ]
                      ...
                  ]
        }
}

戻り値のデータは、入力データについて前述した形式と一致します。AWS では、 HTTP と互換性のあるサービスの規則は、 JSON オブジェクト内に本文を返すことです。これには、 HTTP ステータスコードも含まれます。

この AWS Lambda関数を作成するには、次のステップに従います。

  1. AWS 管理コンソールにログインします(まだログインしていない場合)。

  2. 上記のテンプレートにクラウドプラットフォームアカウント ID を記録します。

  3. 「Lambda」を選択します。

  4. 「関数の作成」を選択します。

  5. 関数名を入力します。

    上記のテンプレートの「Lambda関数名」という行にこの名前を記録します。

  6. 使用する言語を選択します。この例では、Python 3.7を選択します。

  7. この機能の実行ロールを選択または作成します。

    適切なオプションを選択します。通常は、「基本的なLambda権限で新しいロールを作成する」です。

    (このロールは、クラウドアカウントのロールおよびSnowflakeのロールとは別。)

  8. 関数のソースコードを貼り付けることができるウィンドウを開きます。

  9. 関数のコードを入力します。独自の関数をまだ作成していない場合は、デフォルトの関数コードを以下のコードで置き換えることができます。これにより、その入力がエコーされます。カスタム関数を作成する準備ができたら、後でこのコードを置換または更新できます。

    import json
    
    def lambda_handler(event, context):
    
        # 200 is the HTTP status code for "ok".
        status_code = 200
    
        # The return value will contain an array of arrays (one inner array per input row).
        array_of_rows_to_return = [ ]
    
        try:
            # From the input parameter named "event", get the body, which contains
            # the input rows.
            event_body = event["body"]
    
            # Convert the input from a JSON string into a JSON object.
            payload = json.loads(event_body)
            # This is basically an array of arrays. The inner array contains the
            # row number, and a value for each parameter passed to the function.
            rows = payload["data"]
    
            # For each input row in the JSON object...
            for row in rows:
                # Read the input row number (the output row number will be the same).
                row_number = row[0]
    
                # Read the first input parameter's value. For example, this can be a
                # numeric value or a string, or it can be a compound value such as
                # a JSON structure.
                input_value_1 = row[1]
    
                # Read the second input parameter's value.
                input_value_2 = row[2]
    
                # Compose the output based on the input. This simple example
                # merely echoes the input by collecting the values into an array that
                # will be treated as a single VARIANT value.
                output_value = [input_value_1, input_value_2]
    
                # Put the returned row number and the returned value into an array.
                row_to_return = [row_number, output_value]
    
                # ... and add that array to the main array.
                array_of_rows_to_return.append(row_to_return)
    
            json_compatible_string_to_return = json.dumps({"data" : array_of_rows_to_return})
    
        except Exception as err:
            # 400 implies some type of error.
            status_code = 400
            # Tell caller what this function could not handle.
            json_compatible_string_to_return = event_body
    
        # Return the return value and HTTP status code.
        return {
            'statusCode': status_code,
            'body': json_compatible_string_to_return
        }
    
  10. オプション、しかし強く推奨:関数をテストします。

    Snowflakeが提供するサンプルPython関数の場合、次のテストデータを使用します(デフォルトのデータを以下のデータで置換)。

    {
      "body":
        "{ \"data\": [ [ 0, 43, \"page\" ], [ 1, 42, \"life, the universe, and everything\" ] ] }"
    }
    

    実行結果は次のようになります。

    Response:
    {
      "statusCode": 200,
      "body": "{\"data\": [[0, [43, \"page\"]], [1, [42, \"life, the universe, and everything\"]]]}"
    }
    ...
    

上記が正しく機能していれば、外部関数のリモートサービスとして使用できる AWS Lambda関数ができました。

ステップ2:プロキシサービス( AWS の API Gateway)を構成し、 API 統合を(Snowflakeで)作成する

クラウドプラットフォームプロキシサービスとして AWS API Gatewayを構成するには、次のようないくつかのステップが必要です。

  • クラウドプラットフォームアカウントに新しい IAM ロールを作成します。

  • API Gateway(プロキシサービス)を作成し、構成します。

  • プロキシサービスエンドポイントを確保します。

  • Snowflakeで API 統合オブジェクトを作成します。

  • Snowflakeと新しい IAM ロールの間に信頼関係を設定します。

API 統合にはクラウドプラットフォーム(クラウドプラットフォームのロールの ARN)からの情報が必要であり、 API Gatewayには API 統合からの API_AWS_EXTERNAL_ID および API_AWS_IAM_USER_ARN が必要であるため、これらを作成するステップはインターリーブされます。

Cloud Platformアカウントへの新しい IAM ロールの作成

Snowflakeが AWS アカウントを認証するには、Snowflakeが所有する IAM ユーザーに、 AWS アカウントで IAM のロールを引き受ける権限を付与する必要があります。これを実行するには、信頼関係を確立する必要があります。信頼関係を確立するには、 AWS アカウントに IAM ロールを作成し、Snowflakeが所有する IAM ユーザーの ARN で構成する必要があります。また、Snowflakeに API 統合オブジェクトを作成し、 API 統合オブジェクトに、どの IAM のロールを引き受けるかに関する情報を設定する必要があります。

  1. IAM (IDおよびアクセス管理)を介して新しいクラウドプラットフォームのロールを作成します。

  2. 信頼できるエンティティのタイプを選択するように求められたら、「別の AWS アカウント」を選択します。

  3. 「このロールを使用できるアカウントを指定してください」というメッセージが表示されたら、以前に保存したCloud PlatformアカウントIDを貼り付けます。

  4. 「次へ:権限」をクリックします。

  5. 必要に応じて、権限(「権限ポリシーの添付」)を設定します。

  6. ロール名を入力します。

    ロール名を「新しい IAM ロール名」として記録します。

  7. ロールを作成した後、

    • 「ロール ARN 」を「Cloud Platform( IAM)ロール ARN 」として記録します。

API Gatewayで API を作成して構成します(プロキシサービス)。

地域エンドポイントを使用していることを確認します。この機能のプレビューは、 AWS API Gatewayの地域エンドポイントのみをサポートしています。(エンドポイントのさまざまなタイプの説明については、https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-basic-concept.htmlを参照)

Snowflakeが現在サポートしているプロキシサービスのタイプは AWS API Gatewayのみですが、 AWS でホストされていないSnowflakeのインスタンスで外部関数を作成できます。仮想ウェアハウスがAzureまたは GCP 上にある場合、 AWS API Gatewayを介してリモートサービスにアクセスする外部関数を作成できます。

API Gatewayを作成するステップは次のとおりです。

  1. 「API Gateway」を選択します。

  2. 「API の作成」を選択します。

  3. 「REST API」を探し、「ビルド」ボタンをクリックします。

  4. REST プロトコルを選択します。

  5. 「新しい API」オプションを選択します。

  6. 新しい API の作成を完了します。

  7. リソースを作成します。

    リソース名を「プロキシサーバーリソース名」として記録します。後で必要になります。

  8. このリソースの「メソッドの作成」を選択し、 POST オプションを指定します。

    「統合タイプ」は「Lambda関数」である必要があります。

  9. 「Lambdaプロキシ統合を使用する」チェックボックスをクリックします。

  10. 「Lambda関数」フィールドに、先ほど記録したLambda関数名を貼り付けます。

  11. 保存します。

  12. 「API の展開」アクションを選択します。

  13. この関数のステージを選択または作成します。

  14. 「POST」をクリックして、 POST リクエストの「URL の呼び出し」を記録します。これをテンプレートの「リソース呼び出し URL」フィールドに入力します。

    呼び出し URL にリソースの名前が含まれていることを確認します。含まれていない場合は、リソースではなくステージの呼び出し URL をクリックした可能性があります。

AWS API Gatewayプロキシサービスエンドポイントの保護

プロキシサービスエンドポイントの保護の概要については、 プロキシサービスエンドポイントの保護 をご参照ください。

AWS API Gatewayエンドポイントを保護するには、

  1. 「メソッドリクエスト」には「AWS_IAM」認証が必要であることを指定します。

    テンプレートにメソッドリクエスト ARN を記録します。

  2. API Gatewayのリソースポリシーを設定して、ゲートウェイエンドポイントを呼び出すことができるユーザーを指定します。

    リソースポリシーは通常、次のようになります(プリンシパルとリソースをカスタマイズする必要があることを除く)。

    {
        "Version": "2012-10-17",
        "Statement":
        [
            {
            "Effect": "Allow",
            "Principal":
                {
                "AWS": "arn:aws:sts::<12-digit-number>:assumed-role/<external_function_role>/snowflake"
                },
            "Action": "execute-api:Invoke",
            "Resource": "arn:aws:execute-api:us-west-2:123456789012:ljkfds890a/*/POST/MyResourceName"
            }
        ]
    }
    

    リソースポリシーの次の部分を置き換えます。

    • <12桁の数値> をCloud Platform(IAM)ロール ARN からの12桁の数値に置き換えます。通常、これはCloud Platform(IAM)アカウント ID と同じです。

    • <外部関数ロール> をCloud Platform(IAM)ロール ARN からのロール名に置き換えます。通常、これは新しい IAM ロール名と同じです。

      たとえば、Cloud Platform(IAM)のロール名が次の場合、

      arn:aws:iam::987654321098:role/MyNewIAMRole
      

      結果は次のようになります。

      "AWS": "arn:aws:sts::987654321098:assumed-role/MyNewIAMRole/snowflake"
      
    • リソースの POST コマンドの「リソース」をメソッドリクエスト ARN に設定する必要があります。

      注釈

      リソースをメソッドリクエスト ARN に設定すると、プロキシサービスが指定されたリソースへの呼び出しのみを許可するように指定します。メソッドリクエスト ARN のサブセットをプレフィックスとして指定できます。これにより、同じプロキシサービスから複数のリソースを呼び出すことができます。

      たとえば、メソッドリクエスト ARN が次の場合、

      arn:aws:execute-api:us-west-1:123456789012:a1b2c3d4e5/*/POST/MyResource
      

      次のプレフィックスのみを指定できます。

      arn:aws:execute-api:us-west-1:123456789012:a1b2c3d4e5/*/
      
  3. API の展開。

次のいくつかのステップでは、Snowflake API 統合オブジェクトを作成します。ここでクラウドプラットフォーム管理ウィンドウを閉じないでください。後で戻る必要があります。

Snowflakeでの API 統合オブジェクトの作成

  1. Snowflakeセッションを開きます(まだ開いていない場合)。通常は GUI セッションです。

  2. ACCOUNTADMIN 権限を持つSnowflakeロールまたは CREATE INTEGRATION 権限を持つロールを使用します。次に例を示します。

    use role has_accountadmin_privileges;
    
  3. CREATE API INTEGRATION コマンドを入力して、 API 統合を作成します。コマンドは次のようになります。

    CREATE OR REPLACE API INTEGRATION my_api_integration_01
      api_provider = aws_api_gateway
      api_aws_role_arn = '<cloud_platform_role_ARN>'
      enabled = true
      api_allowed_prefixes = ('https://')
    ;
    

    <Cloud Platformロール ARN> は、以前に記録したCloud Platform(IAM)ロール ARN である必要があります。

    API許可のプレフィックスフィールドには、以前に記録したリソース呼び出し URL が含まれている必要があります。

    この例の値を使用するのではなく、 API 統合の名前をカスタマイズすることもできます。

    以下は、包括的な CREATE API INTEGRATION ステートメントの例です。

    create or replace api integration demonstration_external_api_integration_01
        api_provider=aws_api_gateway
        api_aws_role_arn='arn:aws:iam::123456789012:role/my_cloud_account_role'
        api_allowed_prefixes=('https://xyz.execute-api.us-west-2.amazonaws.com/production/')
        enabled=true;
    
  4. 上で入力した CREATE API INTEGRATION コマンドを実行します。

  5. DESCRIBE INTEGRATION コマンドを実行します。

    DESCRIBE INTEGRATION <my_integration_name>;
    

    例:

    DESCRIBE INTEGRATION my_api_integration_01;
    
  6. 「API_AWS_IAM_USER_ARN」という名前のプロパティを探し、そのプロパティのプロパティ値を後で使用できるように記録します。

  7. 「API_AWS_EXTERNAL_ID」という名前のプロパティを探し、そのプロパティのプロパティ値を後で使用できるように記録します。

    API_AWS_EXTERNAL_ID のプロパティ値はしばしば等号(「=」)で終わることに注意してください。その等号は値の一部です。プロパティ値の残りの部分と一緒に切り取って貼り付けてください。

次のいくつかのステップでは、クラウドプラットフォームの管理ウィンドウに戻ります。ここでSnowflake管理ウィンドウを閉じないでください。後で戻る必要があります。

Snowflakeと新しい IAM ロールの間における信頼関係の設定

AWS 管理コンソールで、

  1. IAM を選択します。

  2. ロールを選択します。

  3. 以前に作成して記録した新しい IAM ロール名を探して選択します。

  4. 「信頼関係」タブ、ボタンの順にクリックして信頼関係を編集します。

    これにより、認証情報を追加できるポリシードキュメントが開きます。

  5. ポリシードキュメントで、Statement.Principal.AWS フィールドを見つけ、値(キーではなく)を以前に保存した API_AWS_IAM_USER_ARN に置き換えます。

  6. Statement.Conditionフィールドを探します。最初は、中括弧(「{}」)のみが含まれているはずです。

  7. 中括弧の間に次を貼り付けます。 "StringEquals": { "sts:ExternalId": "xxx" }

  8. 「xxx」を先ほど記録した API_AWS_EXTERNAL_ID の値に置き換えます。

  9. 信頼関係の「ポリシードキュメント」の編集が完了すると、次のようになります。

    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Principal": {
            "AWS": "arn:aws:iam::1234567898012:user/development/development_user"
          },
          "Action": "sts:AssumeRole",
          "Condition": {"StringEquals": { "sts:ExternalId": "EXTERNAL_FUNCTIONS_SFCRole=3_8Hcmbi9halFOkt+MdilPi7rdgOv=" }}
        }
      ]
    }
    
  10. ボタンをクリックして、信頼ポリシーを更新します。

ステップ3:外部関数を作成する

次に、Snowflake管理ウィンドウ(以前に CREATE API INTEGRATION コマンドを入力した場所)に戻ります。

  1. CREATE EXTERNAL FUNCTION コマンドを入力します。次のようになります。

    CREATE EXTERNAL FUNCTION my_external_function(n INTEGER, v VARCHAR)
        RETURNS VARIANT
        API_INTEGRATION = <api_integration_name>
        AS '<invocation_url>'
        ;
    

    <api_integration_name> 値には、以前に作成した API 統合の名前が含まれている必要があります。

    <invocation_url> 値は、以前に記録したリソース呼び出し URL である必要があります。これには、ステージ名だけでなく、リソース名が含まれていることを確認してください。

    この例では2つの引数(INTEGER と VARCHAR)を渡します。これらの引数は、リモートサービスが期待する引数だからです。独自のリモートサービスを作成するときは、リモートサービスに適切な引数を渡します。

  2. 上記で入力した CREATE EXTERNAL FUNCTION コマンドをまだ実行していない場合は、ここで実行してください。

ステップ4:外部関数を呼び出す

  1. 必要に応じて、外部関数の USAGE 権限を1つ以上のロールに付与し、それらのロールが外部関数を呼び出せるようにします。(ロールには、その外部関数に対する USAGE または OWNERSHIP 権限が必要。)

  2. 次を呼び出して関数を実行します。

    SELECT my_external_function(99, 'Luftballoons');
    

トラブルシューティング

症状:

外部関数を呼び出そうとすると、次のエラーメッセージが表示される。

「SQL 実行エラー:AWS_ROLE の想定エラー。AWS ポリシーでロールと外部IDが正しく設定されていることを確認してください。」

考えられる原因:

  • ロールの AWS 信頼関係ポリシーで、 AWS ARN が正しくありません。考えられる原因には、次が含まれます。

    • 設定していない。

    • 設定しているが、Snowflakeの「DESCRIBE INTEGRATION」コマンドから確認できるユーザー ARN ではなく、クラウドプラットフォームのロールの ARN を使用(誤り)。API_AWS_ROLE_ARN ではなく API_AWS_IAM_USER_ARN を使用してください。

  • AWS 信頼関係ポリシーで、std:ExternalId が誤っている。考えられる原因には、次が含まれます。

    • 設定していない。

    • API 統合オブジェクトを再作成した。API オブジェクトを再作成すると、その外部 ID が変更されます。

症状:

外部関数を呼び出そうとすると、次のエラーメッセージが表示される。

外部関数 <関数名> のリクエストに失敗。エラー:403 「{"Message":"User: <ARN> is not authorized to perform: execute-api:Invoke on resource: <MethodRequestARN>"}」

考えられる原因:

API Gatewayのリソースポリシーで誤ったロール ARN を使用している。

考えられる解決策:

テンプレートに従っていることを確認してください。ただし、

  • 12桁の数字は、 IAM アカウント ID に置き換えます。

  • <外部ユーザーロール> は、新しい IAM ロール名に置き換えます。

また、リソースが正しいことを確認してください。メソッドリクエスト ARN である必要があります。