DynamoDB テーブルで定義している API/サービスがあります。いくつかのクエリをサポートするために、いくつかのインデックス (グローバル セカンダリ インデックスとして定義) があります。GSI 定義を使用してテーブルを設計し、正しいクエリのように見えます。ただし、クエリを作成すると、次の例外が発生します。
{ AccessDeniedException: User: arn:aws:sts::OBSCURED:assumed-role/chatroom-application-dev-us-east-1-lambdaRole/chatroom-application-dev-getRoomMessages is not authorized to perform: dynamodb:Query on resource: arn:aws:dynamodb:us-east-1:OBSCURED:table/messages-table-dev/index/roomIndex
at Request.extractError (/var/task/node_modules/aws-sdk/lib/protocol/json.js:48:27)
at Request.callListeners (/var/task/node_modules/aws-sdk/lib/sequential_executor.js:105:20)
at Request.emit (/var/task/node_modules/aws-sdk/lib/sequential_executor.js:77:10)
at Request.emit (/var/task/node_modules/aws-sdk/lib/request.js:683:14)
at Request.transition (/var/task/node_modules/aws-sdk/lib/request.js:22:10)
at AcceptorStateMachine.runTo (/var/task/node_modules/aws-sdk/lib/state_machine.js:14:12)
at /var/task/node_modules/aws-sdk/lib/state_machine.js:26:10
at Request.<anonymous> (/var/task/node_modules/aws-sdk/lib/request.js:38:9)
at Request.<anonymous> (/var/task/node_modules/aws-sdk/lib/request.js:685:12)
at Request.callListeners (/var/task/node_modules/aws-sdk/lib/sequential_executor.js:115:18)
message: 'User: arn:aws:sts::OBSCURED:assumed-role/chatroom-application-dev-us-east-1-lambdaRole/chatroom-application-dev-getRoomMessages is not authorized to perform: dynamodb:Query on resource: arn:aws:dynamodb:us-east-1:OBSCURED:table/messages-table-dev/index/roomIndex',
code: 'AccessDeniedException',
time: 2018-06-02T22:05:46.110Z,
requestId: 'OBSCURED',
statusCode: 400,
retryable: false,
retryDelay: 30.704899664776054 }
例外の上部には、getRoomMessages メソッドis not authorized to perform: dynamodb:Query on resource:
の ARN が表示され、グローバル セカンダリ インデックスの ARN が表示されます。
グローバル セカンダリ インデックスへのアクセス権を付与するポリシーを定義する必要があることは明らかです。しかし、その方法はまったく明らかではありません。DynamoDB に関する他の StackOverflow の質問で、ドキュメントが断片化されていて、何かを見つけるのがいかに難しいかについて不平を言っているのを見てきました。私は同意しなければなりません。「断片化」という言葉は控えめすぎる。
サーバーレス フレームワークを使用しています。このprovider
セクションは、このポリシー/ロールの定義を示しています。
provider:
name: aws
runtime: nodejs8.10
stage: dev
region: us-east-1
iamRoleStatements:
- Effect: Allow
Action:
- dynamodb:Query
- dynamodb:Scan
- dynamodb:GetItem
- dynamodb:PutItem
- dynamodb:UpdateItem
- dynamodb:DeleteItem
Resource:
- { "Fn::GetAtt": ["MessagesDynamoDBTable", "Arn" ] }
- { "Fn::GetAtt": ["#roomIndex", "Arn" ] }
- { "Fn::GetAtt": ["#userIndex", "Arn" ] }
environment:
MESSAGES_TABLE: ${self:custom.tableName}
このResource
セクションでは、許可が宣言されているリソースをリストすることになっていると思います。1 つ目は、テーブル全体を参照します。追加した最後の 2 つは、インデックスを参照します。
編集:実行するとserverless deploy
、次のメッセージが出力されます。
The CloudFormation template is invalid: Template error: instance of Fn::GetAtt references undefined resource #roomIndex
これについていくつかのバリエーションを試しましたが、同じエラーが発生しました。要するに、Cloudfront 構文を使用して、インデックスの ARN を取得するにはどうすればよいかということですserverless.yml
。例外に示されているため、ARN は存在します。
DynamoDB テーブル定義:
resources:
Resources:
MessagesDynamoDBTable:
Type: AWS::DynamoDB::Table
Properties:
AttributeDefinitions:
- AttributeName: messageId
AttributeType: S
- AttributeName: room
AttributeType: S
- AttributeName: userId
AttributeType: S
KeySchema:
- AttributeName: messageId
KeyType: HASH
GlobalSecondaryIndexes:
- IndexName: roomIndex
KeySchema:
- AttributeName: room
KeyType: HASH
Projection:
ProjectionType: ALL
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1
- IndexName: userIndex
KeySchema:
- AttributeName: userId
KeyType: HASH
Projection:
ProjectionType: ALL
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1
TableName: ${self:custom.tableName}
上記の例外に対応して使用されているクエリ:
{
"TableName": "messages-table-dev",
"IndexName": "roomIndex",
"KeyConditionExpression": "#roomIndex = :room",
"ExpressionAttributeNames": {
"#roomIndex": "room"
},
"ExpressionAttributeValues": {
":room": {
"S": "everyone"
}
}
}
そして、クエリを生成する Lambda 関数のコード スニペット:
app.get('/messages/room/:room', (req, res) => {
const params = {
TableName: MESSAGES_TABLE,
IndexName: "roomIndex",
KeyConditionExpression: '#roomIndex = :room',
ExpressionAttributeNames: { '#roomIndex': 'room' },
ExpressionAttributeValues: {
":room": { S: `${req.params.room}` }
},
};
console.log(`QUERY ROOM ${JSON.stringify(params)}`);
dynamoDb.query(params, (error, result) => {
if (error) {
console.log(error);
res.status(400).json({ error: 'Could not get messages' });
} else {
res.json(result.Items);
}
});
});