AppSync で Lambda をデータソースとして DynamoDB にクロスアカウントアクセスしてみた
手順概要
- アカウント A で DynamoDB テーブルを作成
- アカウント B で Lambda 関数を作成
- アカウント B で GraphQL API を作成
- 動作確認
1. アカウント A で DynamoDB テーブルを作成
1-1. アクセス先となるアカウント A に DynamoDB テーブルを作成します。
- パーティションキー: id (文字列)
- ソートキー: なし
- テーブル設定: デフォルト
1-2. テーブルに値を追加します。
- id: 1
- name: test (文字列)
1-3. テーブルにリソースベースのポリシーを定義します。
以下のようなポリシーを定義します。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Statement1",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111111111111:role/LambdaBasicExecutionRole"
},
"Action": "dynamodb:*",
"Resource": "arn:aws:dynamodb:ap-northeast-1:222222222222:table/table-name"
}
]
}
- Principal: アクセス元となる Lambda の IAM ロール
- Lambda 関数作成後に置換してください
- Resource: 作成した DynamoDB テーブルの ARN です。
今回は検証用のため Action を dynamodb:* で指定していますが、適宜最小限のポリシーに変更してください。
2. アカウント B で Lambda 関数を作成
2-1. アクセス元となるアカウント B に Lambda 関数を作成します。
- ランタイム: Node.js 22.x (CommonJS module handler)
- タイムアウト: 5 秒
- IAM ロールの権限: AdministratorAccess ポリシーを付与
- 適宜最小限のポリシーに変更してください
- コードは以下の通り
const { DynamoDBClient } = require('@aws-sdk/client-dynamodb');
const { DynamoDBDocumentClient, GetCommand } = require('@aws-sdk/lib-dynamodb');
exports.handler = async function (event) {
const dynamodb = DynamoDBDocumentClient.from(new DynamoDBClient());
const params = {
TableName: "arn:aws:dynamodb:ap-northeast-1:222222222222:table/table-name",
Key: { id: event.arguments.id }
};
const response = await dynamodb.send(new GetCommand(params));
return {
id: response.Item.id,
name: response.Item.name
}
};
- TableName はアカウント A の DynamoDB テーブル ARN です
- 今回はハードコードしていますが適宜環境変数なども利用してください
- DynamoDB に対して id が 1 のアイテムを返すようにクエリを実行しています
3-2. DynamoDB のリソーベースポリシーを更新
Lambda 関数作成後、Lambda 関数にアタッチされている IAM ロールを確認し、アカウント A の DynamoDB のリソーベースポリシーの Principal に設定してください。
3. アカウント B で GraphQL API を作成
3-1.AppSync コンソールで GraphQL API を作成します。
- API タイプ: GraphQL API
- GraphQL API データソース: Design from scratch
- API 名: 任意の名称
- GraphQL タイプを作成: 後で GraphQL リソースを作成
3-2. データソースを作成します。
- データソース名: 任意の名称
- データソースタイプ: AWS_Lambda
- リージョン: Lambda 関数を作成したリージョン
- 関数: 2 で作成した関数
- 既存のロールを作成または使用する: 新しいロール
3-3. スキーマを作成します。
以下のスキーマを定義しました。
type user {
id: String!
name: String
}
type Query {
getUser(id: String!): user!
}
スキーマの定義後に必ず保存ボタンをクリックしましょう。
3-4. リゾルバーを設定します。
スキーマの画面でリゾルバーの getUser(...): user! の「アタッチ」をクリックします。
- リゾルバータイプ: ユニットリゾルバー
- リゾルバータイプ: AppSync JavaScript (APPSYNC_JS)
- データソース: 3-2 で作成したデータソース
- リゾルバーコードは以下の通り
import { util } from '@aws-appsync/utils';
/**
* Sends a request to a Lambda function. Passes all information about the request from the `info` object.
* @param {import('@aws-appsync/utils').Context} ctx the context
* @returns {import('@aws-appsync/utils').LambdaRequest} the request
*/
export function request(ctx) {
return {
operation: 'Invoke',
payload: {
fieldName: ctx.info.fieldName,
parentTypeName: ctx.info.parentTypeName,
variables: ctx.info.variables,
selectionSetList: ctx.info.selectionSetList,
selectionSetGraphQL: ctx.info.selectionSetGraphQL,
arguments: ctx.arguments,
},
};
}
/**
* Process a Lambda function response
* @param {import('@aws-appsync/utils').Context} ctx the context
* @returns {*} the Lambda function response
*/
export function response(ctx) {
const { result, error } = ctx;
if (error) {
util.error(error.message, error.type, result);
}
return result;
}
ほぼデフォルトの Lambda の呼び出しコードサンプルのままですが、arguments を追加してクエリから Lambda 関数に値を渡せるようにしました。
リゾルバーの定義後に必ず保存ボタンをクリックしましょう。
4. 動作確認
4-1. AppSync コンソールから以下のクエリを実行します。
query MyQuery {
getUser(id: "1") {
id
name
}
}
4-2. 以下の結果を取得できれば成功です。
{
"data": {
"getUser": {
"id": "1",
"name": "test"
}
}
}
まとめ
今回は AppSync で Lambda をデータソースとして DynamoDB にクロスアカウントアクセスしてみました。
どなたかの参考になれば幸いです。
Discussion