Skip to content

Commit 8a1b732

Browse files
committed
feat: support importing DynamoDB tables exported from external stacks
fixes issue #442
1 parent e0a391d commit 8a1b732

File tree

2 files changed

+114
-4
lines changed

2 files changed

+114
-4
lines changed

lib/deploy/stepFunctions/compileIamRole.js

+29-4
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,37 @@ function getSnsPermissions(serverless, state) {
7676
}
7777

7878
function getDynamoDBArn(tableName) {
79-
if (isIntrinsic(tableName) && tableName.Ref) {
79+
if (isIntrinsic(tableName)) {
8080
// most likely we'll see a { Ref: LogicalId }, which we need to map to
8181
// { Fn::GetAtt: [ LogicalId, Arn ] } to get the ARN
82-
return {
83-
'Fn::GetAtt': [tableName.Ref, 'Arn'],
84-
};
82+
if (tableName.Ref) {
83+
return {
84+
'Fn::GetAtt': [tableName.Ref, 'Arn'],
85+
};
86+
}
87+
// but also support importing the table name from an external stack that exports it
88+
// as we still want to support direct state machine actions interacting with those tables
89+
if (tableName['Fn::ImportValue']) {
90+
return {
91+
'Fn::Join': [
92+
':',
93+
[
94+
'arn:aws:dynamodb',
95+
{ Ref: 'AWS::Region' },
96+
{ Ref: 'AWS::AccountId' },
97+
{
98+
'Fn::Join': [
99+
'/',
100+
[
101+
'table',
102+
tableName,
103+
],
104+
],
105+
},
106+
],
107+
],
108+
};
109+
}
85110
}
86111

87112
return {

lib/deploy/stepFunctions/compileIamRole.test.js

+85
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,91 @@ describe('#compileIamRole', () => {
576576
.to.be.deep.equal([worldTableArn]);
577577
});
578578

579+
it('should give dynamodb permission for table name imported from external stack', () => {
580+
const externalHelloTable = { 'Fn::ImportValue': 'HelloStack:Table:Name' };
581+
const helloTableArn = {
582+
'Fn::Join': [
583+
':', ['arn:aws:dynamodb', { Ref: 'AWS::Region' }, { Ref: 'AWS::AccountId' }, { 'Fn::Join': ['/', ['table', externalHelloTable]] }],
584+
],
585+
};
586+
587+
const externalWorldTable = { 'Fn::ImportValue': 'WorldStack:Table:Name' };
588+
const worldTableArn = {
589+
'Fn::Join': [
590+
':', ['arn:aws:dynamodb', { Ref: 'AWS::Region' }, { Ref: 'AWS::AccountId' }, { 'Fn::Join': ['/', ['table', externalWorldTable]] }],
591+
],
592+
};
593+
594+
const genStateMachine = (id, tableName) => ({
595+
id,
596+
definition: {
597+
StartAt: 'A',
598+
States: {
599+
A: {
600+
Type: 'Task',
601+
Resource: 'arn:aws:states:::dynamodb:updateItem',
602+
Parameters: {
603+
TableName: tableName,
604+
},
605+
Next: 'B',
606+
},
607+
B: {
608+
Type: 'Task',
609+
Resource: 'arn:aws:states:::dynamodb:putItem',
610+
Parameters: {
611+
TableName: tableName,
612+
},
613+
Next: 'C',
614+
},
615+
C: {
616+
Type: 'Task',
617+
Resource: 'arn:aws:states:::dynamodb:getItem',
618+
Parameters: {
619+
TableName: tableName,
620+
},
621+
Next: 'D',
622+
},
623+
D: {
624+
Type: 'Task',
625+
Resource: 'arn:aws:states:::dynamodb:deleteItem',
626+
Parameters: {
627+
TableName: tableName,
628+
},
629+
End: true,
630+
},
631+
},
632+
},
633+
});
634+
635+
serverless.service.stepFunctions = {
636+
stateMachines: {
637+
myStateMachine1: genStateMachine('StateMachine1', externalHelloTable),
638+
myStateMachine2: genStateMachine('StateMachine2', externalWorldTable),
639+
},
640+
};
641+
642+
serverlessStepFunctions.compileIamRole();
643+
const resources = serverlessStepFunctions.serverless.service
644+
.provider.compiledCloudFormationTemplate.Resources;
645+
const policy1 = resources.StateMachine1Role.Properties.Policies[0];
646+
const policy2 = resources.StateMachine2Role.Properties.Policies[0];
647+
648+
[policy1, policy2].forEach((policy) => {
649+
expect(policy.PolicyDocument.Statement[0].Action)
650+
.to.be.deep.equal([
651+
'dynamodb:UpdateItem',
652+
'dynamodb:PutItem',
653+
'dynamodb:GetItem',
654+
'dynamodb:DeleteItem',
655+
]);
656+
});
657+
658+
expect(policy1.PolicyDocument.Statement[0].Resource)
659+
.to.be.deep.equal([helloTableArn]);
660+
expect(policy2.PolicyDocument.Statement[0].Resource)
661+
.to.be.deep.equal([worldTableArn]);
662+
});
663+
579664
it('should give dynamodb permission to * whenever TableName.$ is seen', () => {
580665
const helloTable = 'hello';
581666

0 commit comments

Comments
 (0)