Even longer overdue, but the reason my code above adds the lambda permission in runtime is that I couldn’t find how to set a resource policy on a lambda function statically. Since then I’ve found how that can be done, so the code above can be simplified into:
serverless.yml
provider:
[...]
iamRoleStatements:
- Effect: Allow
Action:
- events:putRule
- events:putTargets
- events:deleteRule
- events:removeTargets
[...]
functions:
MyScheduledFunction:
[...]
# Note no events at all, this will be triggered by CW rules created in runtime
resources:
Resources:
MyScheduledFunctionResourcePolicy:
Type: 'AWS::Lambda::Permission'
Properties:
FunctionName:
Fn::GetAtt: [MyScheduledFunctionLambdaFunction, Arn]
Action: 'lambda:invokeFunction'
Principal: 'events.amazonaws.com'
SourceArn:
Fn::Sub: 'arn:aws:events:${AWS::Region}:${AWS::AccountId}:rule/*'
scheduler.js
const functionArn = <my-scheduled-function-arn>;
const ruleName = <rule-name>;
// Add CW rule
await cwe.putRule({
Name: ruleName,
ScheduleExpression: '<cron expression>',
}).promise();
// Tell CW rule to invoke function
await cwe.putTargets({
Rule: ruleName,
Targets: [
{
Id: '<my-target-id>',
Arn: functionArn,
Input: JSON.stringify({
rule: {name: ruleName},
data: { ... }
})
}
]
}).promise();
To avoid adding lots and lots of CW rules, the invoked scheduled function may clean up by removing the rule that triggered it:
my-scheduled-function.js
exports.handler = async event => {
const ruleName = event.rule.name;
try {
[...]
} finally {
await cwe.removeTargets({
Rule: ruleName,
Ids: ['<my-target-id>']
}).promise();
await cwe.deleteRule({
Name: ruleName
}).promise();
}
}