Skip to content

Commit cb803d7

Browse files
feat: support intrinsic functions in lambda::invoke
1 parent 8379b0f commit cb803d7

File tree

2 files changed

+105
-17
lines changed

2 files changed

+105
-17
lines changed

lib/deploy/stepFunctions/compileIamRole.js

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
'use strict';
2+
23
const _ = require('lodash');
34
const BbPromise = require('bluebird');
45
const path = require('path');
@@ -148,27 +149,56 @@ function getLambdaPermissions(state) {
148149
// function name can be name-only, name-only with alias, full arn or partial arn
149150
// https://docs.aws.amazon.com/lambda/latest/dg/API_Invoke.html#API_Invoke_RequestParameters
150151
const functionName = state.Parameters.FunctionName;
151-
const segments = functionName.split(':');
152-
153-
let functionArn;
154-
if (functionName.startsWith('arn:aws:lambda')) {
155-
// full ARN
156-
functionArn = functionName;
157-
} else if (segments.length === 3 && segments[0].match(/^\d+$/)) {
158-
// partial ARN
159-
functionArn = {
160-
'Fn::Sub': `arn:aws:lambda:\${AWS::Region}:${functionName}`,
161-
};
162-
} else {
163-
// name-only (with or without alias)
164-
functionArn = {
165-
'Fn::Sub': `arn:aws:lambda:\${AWS::Region}:\${AWS::AccountId}:function:${functionName}`,
166-
};
152+
if (_.isString(functionName)) {
153+
const segments = functionName.split(':');
154+
155+
let functionArn;
156+
if (functionName.startsWith('arn:aws:lambda')) {
157+
// full ARN
158+
functionArn = functionName;
159+
} else if (segments.length === 3 && segments[0].match(/^\d+$/)) {
160+
// partial ARN
161+
functionArn = {
162+
'Fn::Sub': `arn:aws:lambda:\${AWS::Region}:${functionName}`,
163+
};
164+
} else {
165+
// name-only (with or without alias)
166+
functionArn = {
167+
'Fn::Sub': `arn:aws:lambda:\${AWS::Region}:\${AWS::AccountId}:function:${functionName}`,
168+
};
169+
}
170+
171+
return [{
172+
action: 'lambda:InvokeFunction',
173+
resource: functionArn,
174+
}];
175+
} else if (_.has(functionName, 'Fn::GetAtt')) {
176+
// because the FunctionName parameter can be either a name or ARN
177+
// so you should be able to use Fn::GetAtt here to get the ARN
178+
return [{
179+
action: 'lambda:InvokeFunction',
180+
resource: functionName,
181+
}];
182+
} else if (_.has(functionName, 'Ref')) {
183+
// because the FunctionName parameter can be either a name or ARN
184+
// so you should be able to use Fn::GetAtt here to get the ARN
185+
return [{
186+
action: 'lambda:InvokeFunction',
187+
resource: {
188+
'Fn::Sub': [
189+
'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${FunctionName}',
190+
{
191+
FunctionName: functionName,
192+
},
193+
],
194+
},
195+
}];
167196
}
168197

198+
// hope for the best...
169199
return [{
170200
action: 'lambda:InvokeFunction',
171-
resource: functionArn,
201+
resource: functionName,
172202
}];
173203
}
174204

lib/deploy/stepFunctions/compileIamRole.test.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1205,4 +1205,62 @@ describe('#compileIamRole', () => {
12051205
];
12061206
expect(lambdaPermissions[0].Resource).to.deep.eq(lambdaArns);
12071207
});
1208+
1209+
it('should support intrinsic functions for lambda::invoke resource type', () => {
1210+
const getStateMachine = (name, functionName) => ({
1211+
name,
1212+
definition: {
1213+
StartAt: 'A',
1214+
States: {
1215+
A: {
1216+
Type: 'Task',
1217+
Resource: 'arn:aws:states:::lambda:invoke',
1218+
Parameters: {
1219+
FunctionName: functionName,
1220+
Payload: {
1221+
'ExecutionName.$': '$$.Execution.Name',
1222+
},
1223+
},
1224+
End: true,
1225+
},
1226+
},
1227+
},
1228+
});
1229+
1230+
// function name can be...
1231+
const lambda1 = { Ref: 'MyFunction' }; // name
1232+
const lambda2 = { 'Fn::GetAtt': ['MyFunction', 'Arn'] }; // Arn
1233+
1234+
serverless.service.stepFunctions = {
1235+
stateMachines: {
1236+
myStateMachine1: getStateMachine('sm1', lambda1),
1237+
myStateMachine2: getStateMachine('sm2', lambda2),
1238+
},
1239+
};
1240+
1241+
serverlessStepFunctions.compileIamRole();
1242+
const statements = serverlessStepFunctions.serverless.service
1243+
.provider.compiledCloudFormationTemplate.Resources.IamRoleStateMachineExecution
1244+
.Properties.Policies[0].PolicyDocument.Statement;
1245+
1246+
const lambdaPermissions = statements.filter(s =>
1247+
_.isEqual(s.Action, ['lambda:InvokeFunction']));
1248+
expect(lambdaPermissions).to.have.lengthOf(1);
1249+
1250+
const lambdaArns = [
1251+
{
1252+
'Fn::Sub': [
1253+
'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${FunctionName}',
1254+
{ FunctionName: lambda1 },
1255+
],
1256+
},
1257+
{
1258+
'Fn::GetAtt': [
1259+
'MyFunction',
1260+
'Arn',
1261+
],
1262+
},
1263+
];
1264+
expect(lambdaPermissions[0].Resource).to.deep.eq(lambdaArns);
1265+
});
12081266
});

0 commit comments

Comments
 (0)