Skip to content

Commit 39f7233

Browse files
Merge pull request #214 from horike37/feature/lambda_invoke_intrinsic_funcs
Support intrinsic functions in lambda:invoke resource type
2 parents 8379b0f + d224278 commit 39f7233

File tree

2 files changed

+121
-17
lines changed

2 files changed

+121
-17
lines changed

lib/deploy/stepFunctions/compileIamRole.js

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

197+
// hope for the best...
169198
return [{
170199
action: 'lambda:InvokeFunction',
171-
resource: functionArn,
200+
resource: functionName,
172201
}];
173202
}
174203

lib/deploy/stepFunctions/compileIamRole.test.js

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1205,4 +1205,79 @@ 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+
const lambda3 = { // or, something we don't need special handling for
1234+
'Fn::Sub': [
1235+
'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${FunctionName}',
1236+
{
1237+
FunctionName: 'myFunction',
1238+
},
1239+
],
1240+
};
1241+
1242+
serverless.service.stepFunctions = {
1243+
stateMachines: {
1244+
myStateMachine1: getStateMachine('sm1', lambda1),
1245+
myStateMachine2: getStateMachine('sm2', lambda2),
1246+
myStateMachine3: getStateMachine('sm3', lambda3),
1247+
},
1248+
};
1249+
1250+
serverlessStepFunctions.compileIamRole();
1251+
const statements = serverlessStepFunctions.serverless.service
1252+
.provider.compiledCloudFormationTemplate.Resources.IamRoleStateMachineExecution
1253+
.Properties.Policies[0].PolicyDocument.Statement;
1254+
1255+
const lambdaPermissions = statements.filter(s =>
1256+
_.isEqual(s.Action, ['lambda:InvokeFunction']));
1257+
expect(lambdaPermissions).to.have.lengthOf(1);
1258+
1259+
const lambdaArns = [
1260+
{
1261+
'Fn::Sub': [
1262+
'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${FunctionName}',
1263+
{ FunctionName: lambda1 },
1264+
],
1265+
},
1266+
{
1267+
'Fn::GetAtt': [
1268+
'MyFunction',
1269+
'Arn',
1270+
],
1271+
},
1272+
{
1273+
'Fn::Sub': [
1274+
'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${FunctionName}',
1275+
{
1276+
FunctionName: 'myFunction',
1277+
},
1278+
],
1279+
},
1280+
];
1281+
expect(lambdaPermissions[0].Resource).to.deep.eq(lambdaArns);
1282+
});
12081283
});

0 commit comments

Comments
 (0)