今回は、業務上Lambdaから別アカウントに構築されたAmplifyのDynamoDBテーブルを更新する必要がありその際に少し詰まったので備忘録としてまとめさせていただきました。
構成図としては下のようになっています。
実現したいこと: アカウントAのLambdaからアカウントB上にあるAmplifyのDynamoDBにアクセスする。
参考にしたもの:
Amazon DynamoDB へのクロスアカウントアクセスを設定する
Grant Lambda function access to GraphQL API
目次
- 1 アカウントB上でポリシーを作成する
- 2 アカウントB上でロールを作成する
- 3 アカウントA上でポリシーを作成
- 4 アカウントA上でロールを作成する
- 5 アカウントA上でLambdaを作成
- 6 AccessDenied: User: [arn:aws:sts::[アカウントBのID]:assumed-role/[アカウントBのロール名]/[ロールセッション名]] is not authorized to perform: sts:AssumeRole on resource: [アカウントBのAssumeRoleARN]
- 7 Error: GraphQL error: Not Authorized to access [GraphQL Query] on type Mutation
- 8 まとめ
アカウントB上でポリシーを作成する
まず、アカウントBのAmplify AppSyncアクセスを許可するポリシーを作成します。
アカウントB上でIAMコンソールを開き、ポリシーを作成まで進んでください。
JSONエディタを開き下のように設定します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "appsync:GraphQL" ], "Resource": [ "「AppSync API ARN」/*" ] } ] } |
こんな感じです。
AppSync API ARNを入れてください。
作成できましたら次に進みます。
アカウントB上でロールを作成する
次にアカウントAがアカウントBにアクセスする権限を付与する必要があります。
アカウントB上でIAMコンソールを開き、ロールを作成します。
信頼されたエンティティタイプを選ぶ欄でAWSアカウントを選び、別のアカウントのアカウントIDを入力します。
その後次へを押し先程作成したポリシーをアタッチします。
そうしましたらロールの名前を入力してロールを作成してください。
作成できたらそのロールのARNをメモってください。
このロールは後ほど編集する必要がありますが、一旦次にいきます。
アカウントA上でポリシーを作成
アカウントAでアカウントBにアクセスするためのポリシーを作成する必要があります。
アカウントA上でIAMコンソールを開き、ポリシーを作成します。
JSONエディタで下のように設定します。
1 2 3 4 5 6 7 8 9 10 |
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "sts:AssumeRole", "Resource": "「アカウンBで作成したロールのARN」" } ] } |
先程作成したロールのARNを入れてください。
作成できましたら次に進みます。
アカウントA上でロールを作成する
Lambdaに紐付けるロールを作成します。
信頼されたエンティティタイプでAWSサービスを選びユースケースとしてはLambdaを選択してください。
先程作成したポリシーをアタッチし次へ進みます。
そのまま名前をつけて確認し、作成してください。
アカウントA上でLambdaを作成
では、いよいよLambdaを作成します。
既存のロールを使用するを選んで先程作成したロールを紐づけてください。そうしましたらコーディングします。
ランタイムはNode 16です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
require("isomorphic-fetch"); const AWS = require("aws-sdk"); const AWSAppSyncClient = require("aws-appsync").default; const AUTH_TYPE = require("aws-appsync").AUTH_TYPE; const gql = require("graphql-tag"); const updateTodo = gql(`mutation UpdateTodo( $input: UpdateTodoInput! $condition: ModelTodoConditionInput ) { updateTodo(input: $input, condition: $condition) { id description userId createdAt updatedAt } }`); const getAssumeRole = async () => { let isSuccess = false; try{ // Assume Roleを取得 const sts = new AWS.STS({ apiVersion: "2011-06-15", region: process.env.REGION, }); const stsParams = { RoleArn: process.env.ASSUME_ROLE_ARN, RoleSessionName: "updateTodoSession", DurationSeconds: 900, }; const assumeRoleResponse = await sts.assumeRole(stsParams).promise(); // 認証情報の設定 AWS.config.credentials = new AWS.Credentials({ accessKeyId: assumeRoleResponse.Credentials.AccessKeyId, secretAccessKey: assumeRoleResponse.Credentials.SecretAccessKey, sessionToken: assumeRoleResponse.Credentials.SessionToken }); isSuccess = true; return { isSuccess }; }catch(err){ console.log(`getAssumeRoleError: ${err}`); return { isSuccess }; } }; exports.handler = async (event) => { const { isSuccess } = await getAssumeRole(); if(!isSuccess) return; try{ const appsyncClient = new AWSAppSyncClient({ url: process.env.APPSYNC_URL, region: process.env.REGION, auth: { type: AUTH_TYPE.AWS_IAM, credentials: (AWS.config.credentials ?? null ), }, disableOffline: true, }); const updateParams = { input: { id: 111111111111, description: "hoge" } }; const result = await appsyncClient.mutate({ mutation: updateTodo, variables: updateParams, }); console.log(`updateTodoResult: ${result}`); }catch(err){ console.log(`updateTodoError: ${err}`); } } |
実際に私が業務上作成したLambdaを簡易化したものになります。
コードの説明としてはAssumeRoleを取得した後DynamoDB上のTodoテーブルを更新するLambdaになっています。
環境変数にASSUME_ROLE_ARNとREGIONとAPPSYNC_URLを設定してあげてください。
ひとまずテストしてみます。
私の前に立ちはだかった第一の壁がこのエラーです。
もしかしたらこの記事を見に来てくれた人の中にも出てきたんじゃないでしょうか?
このエラーはarn:aws:sts::[アカウントBのID]:assumed-role/[アカウントBのロール名]/[ロールセッション名]に[アカウントBのAssumeRoleARN]を実行する権限がないことを示しています。
解決方法は、こちらで作成したアカウントBのロールを開き、信頼関係から信頼ポリシーを編集することで解決します。
また、作成した際に別のAWSアカウントIDを記載した際に自動で設定されたルートユーザの情報を信頼ポリシーから削除してアカウントAで実行するLambdaロールのARNを入れましょう。
ルートユーザを信頼関係に含めることは最小権限の原則に則っておらず、アンチパターンとなっております。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": [ "arn:aws:sts::[アカウントBのID]:assumed-role/[このロール名]/[Lambdaで設定するロールセッション名]", - "arn:aws:iam::[アカウントAのID]:root", + {アカウントAで実行するLambdaロールのARN} ] }, "Action": "sts:AssumeRole", "Condition": {} } ] } |
“arn:aws:sts::[アカウントBのID]:assumed-role/[このロール名]/[Lambdaで設定するロールセッション名]”を信頼関係に追加してください。
これでAssumeRoleの取得に関しては解決します。
では、もう一度テストしてみましょう。
次に立ちはだかった第二の壁はこちらです。
要はアクセス権限がないと出ているのですが、ロールの設定もしたしSchema.graphqlでも{ allow: private, provider: iam }を設定してあるしと途方に暮れました。
しかしこちらのエラーは「/amplify/backend/api/[API名]/custom-roles.json」を追加してあげると実行できるようになります。
具体的にはこちらに書いてあります。
1 2 3 4 5 |
{ "adminRoleNames": [ "arn:aws:sts::「アカウントBのID」:assumed-role/「アカウントBのロール名」", ] } |
“arn:aws:sts::「アカウントBのID」:assumed-role/「アカウントBのロール名」”を明示して上げる必要があります。
私はここにアカウントBのロールARNを入れてしまっていてかなり途方に暮れました。
ご注意ください。
「custom-roles.json」が作成できましたら
$ amplify push
して反映させてあげてください。
これで無事DynamoDBを別アカウントからアクセスすることができました。
まとめ
Amplify アプリのDynamoDBテーブルを更新するには、AssumeRoleを作成し、custom-roles.jsonに明示する必要があります。
こんな回りくどいやり方をせずともAppSyncAPIKeyを使用して直接操作する方法もありますが、漏洩した場合のセキュリティリスクとクォータによる制限がありますのであまりおすすめできません。
もし使ってる場合はこちらの記事を参考にAssumeRoleからの方法に変更することをおすすめします。
この記事がどなたかの助けになれば幸いです。
- AWS Bedrockを活用したAI生成テキスト評価と再生成の実装技法 - 2024-06-17
- AWSから公開されたJavaScriptランタイム「LLRT」を使ったLambdaをAWS CDKで構築する方法 - 2024-02-19
- 【Bun】JavaScriptでシェルスクリプトを書けると噂のBun Shell使ってみた - 2024-02-08
- 【Next.js】Next.js 14をAmplify V6でデプロイ・ホスティングする方法【Amplify】 - 2024-02-06
- JavaScript最新の動向【JavaScript Rising Stars2023】 - 2024-02-01