Amazon OpenSearch ServerlessにAWS Lambdaからアクセスできなくて困った話

2024.06.04

ゲームソリューション部の えがわ です。

LambdaからOpenSearch Serverlessへのアクセスに少し詰まってしまったので、備忘録として残しておきます。

最初に結論

以下の設定を行っていませんでした。

  • Lambdaの実行ロールにOpenSearch Serverlessの権限を追加
  • OpenSearch ServerlessのデータアクセスコントロールにLambdaの実行ロールを指定

事前準備

OpenSearch Serverlessのコレクションは作成済みとします。
今回はベクトル検索で試しています。

やったこと

OpenSearch Serverlessのインデックスを作成する処理を実行してみます。
エンドポイントやリージョン、インデックス名は適宜修正してください。

from opensearchpy import OpenSearch, RequestsHttpConnection, AWSV4SignerAuth
import boto3

def lambda_handler(event, context):
    host = 'endpoint'  #without https://
    region = 'us-east-1'
    
    service = 'aoss'
    credentials = boto3.Session().get_credentials()
    auth = AWSV4SignerAuth(credentials, region, service)
    
    client = OpenSearch(
        hosts=[{'host': host, 'port': 443}],
        http_auth=auth,
        use_ssl=True,
        verify_certs=True,
        connection_class=RequestsHttpConnection
    )
    
    index_name = 'index-name'
    client.indices.create(index_name,
        body={
            "settings":{
                "index.knn": True
            },
            "mappings":{
                "properties": {
                    "values": {
                        "type": "knn_vector",
                        "dimension": 1024
                    },
                    "title": {
                        "type": "text"
                    }
                }
            }
        }
    )

AWS SDK for Pandasのレイヤー追加しておくとopensearch-pyも使用できます。

Lambdaをそのまま実行してみると以下のエラーが発生します。

{
  "errorMessage": "AuthorizationException(403, 'Forbidden')",
  "errorType": "AuthorizationException",
  "requestId": "fe4c4987-3ab5-4264-b317-b06249d1c11b",
  "stackTrace": [
    "  File \"/var/task/lambda_function.py\", line 22, in lambda_handler\n    client.indices.create(index_name,\n",
    "  File \"/opt/python/opensearchpy/client/utils.py\", line 180, in _wrapped\n    return func(*args, params=params, headers=headers, **kwargs)\n",
    "  File \"/opt/python/opensearchpy/client/indices.py\", line 164, in create\n    return self.transport.perform_request(\n",
    "  File \"/opt/python/opensearchpy/transport.py\", line 447, in perform_request\n    raise e\n",
    "  File \"/opt/python/opensearchpy/transport.py\", line 408, in perform_request\n    status, headers_response, data = connection.perform_request(\n",
    "  File \"/opt/python/opensearchpy/connection/http_requests.py\", line 231, in perform_request\n    self._raise_error(\n",
    "  File \"/opt/python/opensearchpy/connection/base.py\", line 315, in _raise_error\n    raise HTTP_EXCEPTIONS.get(status_code, TransportError)(\n"
  ]
}

ははーん、このエラーはLambdaの実行ロールのポリシーですね!?
ということで、実行ロールにポリシーを追加します。
↓のポリシー追加は後に分かりますが間違った手順です。

実行ロールを選択し

OpenSearchServiceのポリシーを追加します。
とりあえず困ったときのFullAccessを追加しています。

再度Lambdaを実行します。

{
  "errorMessage": "AuthorizationException(403, 'Forbidden')",
  "errorType": "AuthorizationException",
  "requestId": "a3bb8f14-5e71-49d1-9f67-f9e341a97393",
  "stackTrace": [
    "  File \"/var/task/lambda_function.py\", line 22, in lambda_handler\n    client.indices.create(index_name,\n",
    "  File \"/opt/python/opensearchpy/client/utils.py\", line 180, in _wrapped\n    return func(*args, params=params, headers=headers, **kwargs)\n",
    "  File \"/opt/python/opensearchpy/client/indices.py\", line 164, in create\n    return self.transport.perform_request(\n",
    "  File \"/opt/python/opensearchpy/transport.py\", line 447, in perform_request\n    raise e\n",
    "  File \"/opt/python/opensearchpy/transport.py\", line 408, in perform_request\n    status, headers_response, data = connection.perform_request(\n",
    "  File \"/opt/python/opensearchpy/connection/http_requests.py\", line 231, in perform_request\n    self._raise_error(\n",
    "  File \"/opt/python/opensearchpy/connection/base.py\", line 315, in _raise_error\n    raise HTTP_EXCEPTIONS.get(status_code, TransportError)(\n"
  ]
}

なにも変わらない...
先ほど追加したポリシーを確認すると"es"のFullAccessを付与していますが、今回はServerlessなので"aoss"の付与を行う必要がありそうです。
AWSは提供してなさそうにみえるので、インラインポリシーを作成からポリシーを作成していきます。

OpenSearch Serverlessのポリシーを検索し、またまた困ったときのFullAccessを追加してみます。

再度Lambdaを実行します。

{
  "errorMessage": "AuthorizationException(403, 'security_exception', 'OpenSearch exception [type=authorization_exception, reason=User does not have permissions for the requested resource]')",
  "errorType": "AuthorizationException",
  "requestId": "be8f0954-a261-4b0b-acbd-704863c54860",
  "stackTrace": [
    "  File \"/var/task/lambda_function.py\", line 22, in lambda_handler\n    client.indices.create(index_name,\n",
    "  File \"/opt/python/opensearchpy/client/utils.py\", line 180, in _wrapped\n    return func(*args, params=params, headers=headers, **kwargs)\n",
    "  File \"/opt/python/opensearchpy/client/indices.py\", line 164, in create\n    return self.transport.perform_request(\n",
    "  File \"/opt/python/opensearchpy/transport.py\", line 447, in perform_request\n    raise e\n",
    "  File \"/opt/python/opensearchpy/transport.py\", line 408, in perform_request\n    status, headers_response, data = connection.perform_request(\n",
    "  File \"/opt/python/opensearchpy/connection/http_requests.py\", line 231, in perform_request\n    self._raise_error(\n",
    "  File \"/opt/python/opensearchpy/connection/base.py\", line 315, in _raise_error\n    raise HTTP_EXCEPTIONS.get(status_code, TransportError)(\n"
  ]
}

403は継続して発生していますが、エラーは変わりました!
OpenSearch Serverlessへ近づいているように見えます!

コレクションの権限周りに問題がありそうなので、マネジメントコンソールを眺めてみます。

セキュリティのデータアクセスポリシーが怪しそうな気がするので、ポリシーを確認してみます。

選択されたプリンシパルにコレクションを作成したロールが設定されています。
ここにLambdaの実行ロールを設定すればうまく動きそうな予感がしています。

再度Lambdaを実行します。
成功しました!

インデックスも追加されています。

最後に

今回、AWS LambdaからAmazon OpenSearch Serverlessへアクセスする際の困った内容について記載しました。
この記事がどなたかの参考になれば幸いです。