はじめに
本記事では、Amazon OpenSearch Serverless に対して、別アカウントの AWS Lambda からアクセスする方法を解説します。あまり一般的なユースケースではないかもしれませんが、特定の要件で必要になる場合があります。
OpenSearch Serverless は、データアクセスポリシーによってアクセスできる対象 (プリンシパル) を制限します。プリンシパルに指定できるのは、IAM ユーザーや IAM ロールの ARN、もしくは SAML アイデンティティです。
しかし、ドキュメントに記載の通り、OpenSearch Serverless のデータアクセスポリシーは他 AWS アカウントのプリンシパルを直接追加できません。そのため、クロスアカウントでのアクセスには工夫が必要です。本記事では、IAM ロールの引き受け(AssumeRole)を利用した方法を紹介します。
クロスアカウントデータアクセス
クロスアカウント ID またはクロスアカウントコレクションを使用してデータアクセスポリシーを作成することはできませんが、ロールの引き受けオプションを使用してクロスアカウントアクセスを設定することもできます。例えば、account-b がアクセスする必要があるコレクションを account-a が所有している場合、account-b のユーザーは account-a でロールを引き受けることができます。ロールには IAM アクセス許可、aoss:APIAccessAll および aoss:DashboardsAccessAll があり、account-a のデータアクセスポリシーに含まれている必要があります。
本記事で作成するアーキテクチャと処理の流れ
以下のアーキテクチャを構築していきます。
- アカウント A にある Lambda が、アカウント B で作成された IAM ロールを Assume します
- アカウント B の IAM ロールは OpenSearch Serverless のデータアクセスポリシーにプリンシパルとして指定されています。これにより アカウント A の Lambda が OpenSearch Serverless にアクセスできます
注意 : クロスアカウントでのアクセスを推奨するという訳ではなく、あくまで方法の一つとして紹介します。例えば、Lambda は OpenSearch Serverless と同じアカウントに配置して、Lambda の呼び出しを別アカウントから行う方が構成としてシンプルになる場合も考えられるため、要件に応じて採用するアーキテクチャを検討してください。
やってみる
re:Post の記事を参考に、クロスアカウントでの Lambda の AssumeRole を実装していきます。
2 つのアカウントを行き来するため、混乱しないように OpenSearch Serverless をデプロイするアカウントを [AOSS アカウント]、Lambda をデプロイするアカウントを [Lambda アカウント] として、各手順がどちらのアカウントでの操作であるかを示します。
1. [AOSS アカウント] OpenSearch Serverless コレクションの作成
-
OpenSearch Service コンソールから以下の設定でコレクションを作成します
- コレクション名:
lambda-assume-test-collection
- コレクションタイプ: 検索
- デプロイタイプ: アクティブレプリカを無効化
- コレクション名:
-
作成後に払い出されるエンドポイントは後ほど利用するのでコピーしておきます
2. [AOSS アカウント] OpenSearch Serverless 用の IAM ポリシーと、IAM ロールを作成する
-
以下のポリシーを持つ IAM ポリシーを作成します。ポリシー名は
aoss-full-access-policy
とします{ "Version": "2012-10-17", "Statement": [ { "Sid": "AOSSFullAccessPolicy", "Effect": "Allow", "Action": "aoss:*", "Resource": "*" } ] }
-
作成した許可ポリシー (
aoss-full-access-policy
) を持つ IAM ロールを作成します。信頼関係などは後ほど編集するため、ひとまず以下のように設定します
3. [AOSS アカウント] 作成した IAM ロールをOpenSearch Serverless のデータアクセスポリシーに追加する
- 作成した OpenSearch Serverless (
lambda-assume-test-collection
) のデータアクセスポリシーの編集画面から、先ほど作成したlambda-assume-test-role
を追加します
4. [Lambda アカウント] Lambda 関数を作成する
-
以下の設定で Lambda を作成します
-
aws-sdk-pandas をレイヤーに追加する
- 今回は opensearchpy を利用するので、レイヤーを追加する必要があります。aws-sdk-pandas を利用すると簡単なので、今回はこちらを使います
- ここから、Python のバージョン (Python 3.10)、アーキテクチャ (x86_64)、リージョン (Lambda を作成したリージョン) に合致するレイヤーの ARN を Lambda に追加します。 ARN を使用する Lambda レイヤーの追加方法の詳細はこちらのドキュメントを参照してください
-
環境変数を以下の通り設定します
-
以下のコードを
lambda_function.py
に貼り付けて Lambda をデプロイしますfrom opensearchpy import OpenSearch, RequestsHttpConnection, AWSV4SignerAuth import os import boto3 # OpenSearchのホスト情報 host = os.environ["AOSS_HOST"] region = os.environ["AOSS_REGION"] service = 'aoss' # AWS STSを使用して一時的な認証情報を取得 sts = boto3.client("sts", region_name=region) res = sts.assume_role( RoleArn=os.environ["AOSS_ACCESS_ROLE_ARN"], RoleSessionName="example-session", ) print(res) # セッションを作成 session = boto3.Session( aws_access_key_id=res["Credentials"]["AccessKeyId"], aws_secret_access_key=res["Credentials"]["SecretAccessKey"], aws_session_token=res["Credentials"]["SessionToken"], region_name=region ) # OpenSearchクライアントの設定 auth = AWSV4SignerAuth(session.get_credentials(), region, service) client = OpenSearch( hosts=[{'host': host, 'port': 443}], http_auth=auth, use_ssl=True, verify_certs=True, connection_class=RequestsHttpConnection ) def lambda_handler(event, context): index_name = 'lambda-access-index' # インデックスが存在しない場合は作成 if not client.indices.exists(index_name): client.indices.create(index_name, body={ "mappings": { "properties": { "title": { "type": "text" } } } } ) # ドキュメントの投入 document = { "title": "Sample Document Title" } response = client.index( index=index_name, body=document, ) print("put item") print(response) # インデックスの検索 query = { 'size': 5, 'query': { 'match_all': {} } } response = client.search( body=query, index=index_name ) print("search") print(response) return { 'statusCode': 200, 'body': response }
5. [Lambda アカウント] Lambda の実行ロールを編集し、AOSS アカウントで作成した IAM ロールを Assume できるようにする
-
Lambda の実行ロールを編集します。アクセス権限のページから Lambda に設定された IAM ロールを選択します
-
Lambda の許可ポリシーの末尾に以下の記述を追加します。Resource は、手順 2 でコピーした
lambda-assume-test-role
の ARN に置き換えてください{ "Effect": "Allow", "Action": "sts:AssumeRole", "Resource": "arn:aws:iam::{AOSS_ACCOUNT}:role/{ROLE_NAME}" }
-
編集後のポリシーは以下のようなイメージになります
-
Lambda 実行ロールの ARN は次の手順で利用するので、コピーしておきます
6. [AOSS アカウント] IAM ロールの信頼関係に Lambda 実行ロールを追加する
-
lambda-assume-test-role
の信頼関係は以下のようになっているはずです。これを編集していきます -
「信頼ポリシーを編集」から、信頼ポリシーを以下の通り変更します。Principal は、手順 5 でコピーした Lambda 実行ロールの ARN に置き換えてください
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::{LAMBDA_ACCOUNT}:role/service-role/{LAMBDA_ROLE_NAME}" }, "Action": "sts:AssumeRole" } ] }
7. [Lambda アカウント] Lambda を実行して動作確認する
-
Lambda コンソールから関数をテストします。この関数は、作成した OpenSearch Serverless に対して以下の操作を順番に実施します
-
lambda-access-index
という名前のインデックスを作成 - サンプルのドキュメントを 1 つ書き込む
- 書き込んだドキュメントを検索する
-
-
上手く設定出来ていれば以下のように別アカウントの OpenSearch Serverless に対するクエリ結果が返ってきます!タイムアウトエラーが発生する場合はタイムアウトを 30 秒ほどに伸ばして再度試してみてください
まとめ
今回は別アカウントの Lambda から OpenSearch Serverless を操作してみました。レアなケースではありますが、参考になれば幸いです。