Skip to content

Commit 28fa07b

Browse files
authored
Merge pull request #568 from aligent/support-batch-dynamodb-task
feat: add support dynamoDB batch tasks using AWS SDK service integration
2 parents f6a7d9c + d31d658 commit 28fa07b

File tree

2 files changed

+143
-0
lines changed

2 files changed

+143
-0
lines changed

lib/deploy/stepFunctions/compileIamRole.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,28 @@ function getDynamoDBPermissions(action, state) {
222222
resource,
223223
}];
224224
}
225+
226+
function getBatchDynamoDBPermissions(action, state) {
227+
if (state.Parameters['RequestItems.$']) {
228+
// When the RequestItems object is only known at runtime,
229+
// we have to provide * permissions during deployment.
230+
return [{
231+
action,
232+
resource: '*',
233+
}];
234+
}
235+
// If RequestItems is specified it must contain the target
236+
// table names as keys. We can use these to generate roles
237+
// whether the array of requests for that table is known
238+
// at deploy time or not
239+
const tableNames = Object.keys(state.Parameters.RequestItems);
240+
241+
return tableNames.map(tableName => ({
242+
action,
243+
resource: getDynamoDBArn(tableName.replace('.$', '')),
244+
}));
245+
}
246+
225247
function getRedshiftDataPermissions(action, state) {
226248
if (['redshift-data:ExecuteStatement', 'redshift-data:BatchExecuteStatement'].includes(action)) {
227249
const clusterName = _.has(state, 'Parameters.ClusterIdentifier') ? state.Parameters.ClusterIdentifier : '*';
@@ -520,6 +542,11 @@ function getIamPermissions(taskStates) {
520542
case 'arn:aws:states:::aws-sdk:dynamodb:query':
521543
return getDynamoDBPermissions('dynamodb:Query', state);
522544

545+
case 'arn:aws:states:::aws-sdk:dynamodb:batchGetItem':
546+
return getBatchDynamoDBPermissions('dynamodb:BatchGetItem', state);
547+
case 'arn:aws:states:::aws-sdk:dynamodb:batchWriteItem':
548+
return getBatchDynamoDBPermissions('dynamodb:BatchWriteItem', state);
549+
523550
case 'arn:aws:states:::aws-sdk:redshiftdata:executeStatement':
524551
return getRedshiftDataPermissions('redshift-data:ExecuteStatement', state);
525552
case 'arn:aws:states:::aws-sdk:redshiftdata:batchExecuteStatement':

lib/deploy/stepFunctions/compileIamRole.test.js

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -908,6 +908,122 @@ describe('#compileIamRole', () => {
908908
expect(policy.PolicyDocument.Statement[0].Resource[0]).to.equal('*');
909909
});
910910

911+
it('should give batch dynamodb permission for only tables referenced by state machine', () => {
912+
const helloTable = 'hello';
913+
const helloTableArn = {
914+
'Fn::Join': [
915+
':', ['arn', { Ref: 'AWS::Partition' }, 'dynamodb', { Ref: 'AWS::Region' }, { Ref: 'AWS::AccountId' }, 'table/hello'],
916+
],
917+
};
918+
const worldTable = 'world';
919+
const worldTableArn = {
920+
'Fn::Join': [
921+
':', ['arn', { Ref: 'AWS::Partition' }, 'dynamodb', { Ref: 'AWS::Region' }, { Ref: 'AWS::AccountId' }, 'table/world'],
922+
],
923+
};
924+
925+
const genStateMachine = (id, tableName) => ({
926+
id,
927+
definition: {
928+
StartAt: 'A',
929+
States: {
930+
A: {
931+
Type: 'Task',
932+
Resource: 'arn:aws:states:::aws-sdk:dynamodb:batchWriteItem',
933+
Parameters: {
934+
RequestItems: {
935+
[tableName]: [],
936+
},
937+
},
938+
Next: 'B',
939+
},
940+
B: {
941+
Type: 'Task',
942+
Resource: 'arn:aws:states:::aws-sdk:dynamodb:batchGetItem',
943+
Parameters: {
944+
RequestItems: {
945+
[tableName]: {},
946+
},
947+
},
948+
End: true,
949+
},
950+
},
951+
},
952+
});
953+
serverless.service.stepFunctions = {
954+
stateMachines: {
955+
myStateMachine1: genStateMachine('StateMachine1', helloTable),
956+
myStateMachine2: genStateMachine('StateMachine2', worldTable),
957+
},
958+
};
959+
960+
serverlessStepFunctions.compileIamRole();
961+
const resources = serverlessStepFunctions.serverless.service
962+
.provider.compiledCloudFormationTemplate.Resources;
963+
const policy1 = resources.StateMachine1Role.Properties.Policies[0];
964+
const policy2 = resources.StateMachine2Role.Properties.Policies[0];
965+
966+
[policy1, policy2].forEach((policy) => {
967+
expect(policy.PolicyDocument.Statement[0].Action)
968+
.to.be.deep.equal([
969+
'dynamodb:BatchWriteItem',
970+
'dynamodb:BatchGetItem',
971+
]);
972+
});
973+
974+
expect(policy1.PolicyDocument.Statement[0].Resource)
975+
.to.be.deep.equal([helloTableArn]);
976+
expect(policy2.PolicyDocument.Statement[0].Resource)
977+
.to.be.deep.equal([worldTableArn]);
978+
});
979+
980+
it('should give batch dynamodb permission to * whenever RequestItems.$ is seen', () => {
981+
const genStateMachine = id => ({
982+
id,
983+
definition: {
984+
StartAt: 'A',
985+
States: {
986+
A: {
987+
Type: 'Task',
988+
Resource: 'arn:aws:states:::aws-sdk:dynamodb:batchWriteItem',
989+
Parameters: {
990+
RequestItems: {
991+
tableName: [],
992+
},
993+
},
994+
Next: 'B',
995+
},
996+
B: {
997+
Type: 'Task',
998+
Resource: 'arn:aws:states:::aws-sdk:dynamodb:batchWriteItem',
999+
Parameters: {
1000+
'RequestItems.$': '$.requestItems',
1001+
},
1002+
End: true,
1003+
},
1004+
},
1005+
},
1006+
});
1007+
1008+
serverless.service.stepFunctions = {
1009+
stateMachines: {
1010+
myStateMachine1: genStateMachine('StateMachine1'),
1011+
},
1012+
};
1013+
1014+
serverlessStepFunctions.compileIamRole();
1015+
const policy = serverlessStepFunctions.serverless.service
1016+
.provider.compiledCloudFormationTemplate.Resources.StateMachine1Role
1017+
.Properties.Policies[0];
1018+
expect(policy.PolicyDocument.Statement[0].Action)
1019+
.to.be.deep.equal(['dynamodb:BatchWriteItem']);
1020+
1021+
// even though some tasks target specific tables, because RequestItems.$ is used we
1022+
// have to give broad permissions to allow execution to talk to whatever table
1023+
// the input specifies
1024+
expect(policy.PolicyDocument.Statement[0].Resource).to.equal('*');
1025+
});
1026+
9111027
it('should give Redshift Data permissions to * for safe actions', () => {
9121028
serverless.service.stepFunctions = {
9131029
stateMachines: {

0 commit comments

Comments
 (0)