Skip to content

Instantly share code, notes, and snippets.

@jagchat
Last active August 16, 2023 14:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jagchat/f8cf70e877d14996a3dc66078e92d361 to your computer and use it in GitHub Desktop.
Save jagchat/f8cf70e877d14996a3dc66078e92d361 to your computer and use it in GitHub Desktop.
const lambdaHelpers = require('lambda-helpers');
const eventHelpers = require('event-helpers');
const utils = require('utils');
class BaseLambdaImplementation {
constructor(event) {
this.event = event;
console.log("Event info---------");
console.log(JSON.stringify(this.event));
try {
if (this.event.hasOwnProperty('body')) { //api gateway lambda helper
this.reqId = utils.getNewId();
this.payload = JSON.parse(this.event.body) || {};
this.nextEventState = { ReqId: this.reqId };
}
else if (this.event.hasOwnProperty('source')) {//event consumer lambda
this.inputEvent = this.event.source; //triggered event
this.inputEventBody = this.event.detail || {};
this.inputEventState = this.inputEventBody.state;
this.payload = this.inputEventBody.payload;
this.nextEventState = this.inputEventState || {};
this.reqId = this.inputEventState.ReqId;
}
this.msg = lambdaHelpers.getSuccessResult({ ReqId: this.reqId, payload: this.payload });
}
catch (err) {
console.log("ERROR :: BaseLambdaImplementation.constructor Error ---");
console.log(err);
}
}
async raiseEvent(eventParam) {
try {
eventParam = {
DetailType: 'schedule.info',
EventBusName: process.env.SchedulerOperationsBusName,
Detail: JSON.stringify({
state: this.nextEventState,
payload: this.payload
}),
...eventParam
};
let eventResult = await eventHelpers.raiseEvent(eventParam);
if (eventResult.isSuccess) {
//this.msg = lambdaHelpers.getSuccessResult({ ReqId: this.reqId, payload: this.payload });
}
else {
this.msg = lambdaHelpers.getExceptionResult({ ReqId: this.reqId, Message: "Not able to raise event", ErrorObject: eventResult.err, Data: eventParam });
}
}
catch (err) {
console.log("ERROR :: BaseLambdaImplementation.raiseEvent Error ---");
console.log(err);
}
}
updateHandlerResultSuccessMsg(data) {
this.msg = lambdaHelpers.getSuccessResult({ ReqId: this.reqId, ...data });
}
updateHandlerResultErrorMsg(data) {
this.msg = lambdaHelpers.getErrorResult({ ReqId: this.reqId, ...data });
}
doCallback(callback) {
callback(null, this.msg);
}
}
//export { BaseLambdaImplementation }
module.exports = BaseLambdaImplementation;
"use strict";
import { serializeError } from 'serialize-error';
import { SchedulerClient, GetScheduleCommand, CreateScheduleCommand, FlexibleTimeWindowMode } from "@aws-sdk/client-scheduler";
import BaseLambdaImplementation from 'base-implementation';
import moment from 'moment';
class LambdaImplementation extends BaseLambdaImplementation {
constructor(event) {
super(event);
}
//https://docs.aws.amazon.com/scheduler/latest/UserGuide/schedule-types.html#cron-based
createSchedule = async (payload) => {
let schClient = new SchedulerClient({ region: payload.Detail.ScheduleHeader.RegionEndPoint });
let params = {
Name: process.env.EnvironmentPrefix + "-" + payload.Detail?.ScheduleHeader?.TenantId + "-" + payload.Name,
//GroupName: "Test", //TODO
ScheduleExpression: payload.ScheduleExpression,
FlexibleTimeWindow: { Mode: FlexibleTimeWindowMode.OFF },
Target: {
Arn: "arn:aws:scheduler:::aws-sdk:eventbridge:putEvents",
RoleArn: process.env.SchedulerRoleUrn,
Input: JSON.stringify({
Entries: [{
EventBusName: process.env.ScheduledEventsBusName,
Source: "p360.schedule.event",
DetailType: "schedule.event.info",
Detail: JSON.stringify(payload)
}]
}),
},
};
if (payload.StartDate &&
moment(payload.StartDate, "YYYY-MM-DDTHH:mm:ssZ", true).isValid()) {
params.StartDate = new Date(payload.StartDate);
}
if (payload.EndDate &&
moment(payload.EndDate, "YYYY-MM-DDTHH:mm:ssZ", true).isValid()) {
params.EndDate = new Date(payload.EndDate);
}
let result = await schClient.send(new CreateScheduleCommand(params));
}
async handler(event, context, callback) {
console.log("Handler: Started...");
let eventNext = ""; //for next event to trigger as part of execution workflow
try {
var response = await this.createSchedule(this.payload);
eventNext = 'p360.scheduler.CreateScheduleAPI.CreatedScheduleInEventBridge';
}
catch (err) {
console.log("ERROR :: at create-schedule-in-eb ---");
console.log(err);
eventNext = 'p360.scheduler.CreateScheduleAPI.ErrorCreatingScheduleInEventBridge';
this.nextEventState.ErrorMsg = "Unable to create schedule in EventBridge";
this.nextEventState.ErrorInfo = serializeError(err);
}
await super.raiseEvent({
Source: eventNext
});
console.log("Handler: Completed...");
this.doCallback(callback);
}
}
export const handler = async (event, context, callback) => {
const impl = new LambdaImplementation(event);
return await impl.handler(event, context, callback);
};
const { EventBridgeClient, PutEventsCommand } = require('@aws-sdk/client-eventbridge');
const eventHelpers = {
raiseEvent: async function (eventParam) {
try {
const params = {
"Entries": [eventParam]
};
const ebClient = new EventBridgeClient();
const response = await ebClient.send(new PutEventsCommand(params));
return { isSuccess: true };
}
catch (err) {
console.log("ERROR :: eventHelpers.raiseEvent ---", err);
console.log(err);
return { isSuccess: false, err: err };
}
}
};
module.exports = eventHelpers;
const serializeError = require('serialize-error');
const lambdaHelpers = {
getSuccessResult: function (content) {
return {
isBase64Encoded: false,
body: JSON.stringify(content),
headers: {
'Access-Control-Allow-Origin': '*',
},
statusCode: 200,
}
},
getErrorResult: function (content) {
content.isError = true;
return {
isBase64Encoded: false,
body: JSON.stringify(content),
headers: {
'Access-Control-Allow-Origin': '*',
},
//statusCode: 500,
statusCode: 200,
}
},
getExceptionResult: function (content) {
let item = {
ReqId: content.ReqId || "",
isError: true,
Message: content.Message || "",
Data: content.Data || {},
ErrorInfo: serializeError(content.ErrorObject)
};
return {
isBase64Encoded: false,
body: JSON.stringify(item),
headers: {
'Access-Control-Allow-Origin': '*',
},
//statusCode: 500,
statusCode: 200,
}
}
};
module.exports = lambdaHelpers;
locals {
computed_sch_operations_bus_name = "${var.namespace}-${var.scheduler_operations_bus_name}"
}
resource "aws_iam_role" "create_schedule_in_eb_lambda_role" {
name = "${var.namespace}-create_schedule_in_eb_lambda_role"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": [
"lambda.amazonaws.com"
]
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
resource "aws_iam_policy" "create_schedule_in_eb_lambda_policy" {
name = "${var.namespace}-create_schedule_in_eb_lambda_policy"
path = "/"
description = "AWS IAM Policy for managing aws lambda role"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:*",
"Effect": "Allow"
},
{
"Effect": "Allow",
"Action": [
"events:PutEvents"
],
"Resource": [
"arn:aws:events:*:*"
]
},
{
"Effect": "Allow",
"Action": [
"scheduler:CreateSchedule",
"scheduler:GetSchedule",
"scheduler:UpdateSchedule"
],
"Resource": "arn:aws:scheduler:*:*:schedule/*/*"
},
{
"Effect": "Allow",
"Action": "iam:PassRole",
"Resource": "arn:aws:iam::*:role/*",
"Condition": {
"StringLike": {
"iam:PassedToService": "scheduler.amazonaws.com"
}
}
}
]
}
EOF
}
resource "aws_iam_role_policy_attachment" "create_schedule_in_eb_lambda_attach_iam_policy_to_iam_role" {
role = aws_iam_role.create_schedule_in_eb_lambda_role.name
policy_arn = aws_iam_policy.create_schedule_in_eb_lambda_policy.arn
}
data "archive_file" "create_schedule_in_eb_lambda_app_zip" {
type = "zip"
source_dir = "${path.module}/app"
#source_file = "index.js" #if one file
output_path = "${path.module}/app.zip"
}
data "aws_iam_role" "scheduler_role" {
name = "${var.namespace}-scheduler_role"
}
data "aws_lambda_layer_version" "layer_default" {
layer_name = "${var.namespace}-layer-default"
}
data "aws_lambda_layer_version" "layer_base" {
layer_name = "${var.namespace}-layer-base"
}
resource "aws_lambda_function" "create_schedule_in_eb_lambda_function" {
filename = "${path.module}/app.zip"
function_name = "${var.namespace}-create_schedule_in_eb_lambda"
role = aws_iam_role.create_schedule_in_eb_lambda_role.arn
handler = "index.handler"
source_code_hash = data.archive_file.create_schedule_in_eb_lambda_app_zip.output_base64sha256
runtime = "nodejs18.x"
environment {
variables = {
ScheduledEventsBusName = "${var.namespace}-${var.scheduled_events_bus_name}"
SchedulerOperationsBusName = local.computed_sch_operations_bus_name
SchedulerRoleUrn = "${data.aws_iam_role.scheduler_role.arn}"
EnvironmentPrefix = "${var.namespace}"
}
}
depends_on = [
aws_iam_role_policy_attachment.create_schedule_in_eb_lambda_attach_iam_policy_to_iam_role
]
layers = [
data.aws_lambda_layer_version.layer_default.arn,
data.aws_lambda_layer_version.layer_base.arn
]
}
resource "aws_cloudwatch_log_group" "create_schedule_in_eb_function_log_group" {
name = "/aws/lambda/${aws_lambda_function.create_schedule_in_eb_lambda_function.function_name}"
retention_in_days = var.lambda_logs_retention
lifecycle {
prevent_destroy = false
}
}
//subscribe to cloudwatch event rule
resource "aws_cloudwatch_event_rule" "create_schedule_in_eb_event_sub_rule" {
name = "${var.namespace}-create_schedule_in_eb_event_sub"
description = "Capture each AWS Console Sign In"
event_bus_name = local.computed_sch_operations_bus_name
event_pattern = <<EOF
{
"source": ["p360.scheduler.CreateScheduleAPI.CreatedScheduleInS3"],
"detail-type": ["schedule.info"]
}
EOF
}
resource "aws_cloudwatch_event_target" "create_schedule_in_eb_event_sub_rule_lambda_target" {
arn = aws_lambda_function.create_schedule_in_eb_lambda_function.arn
rule = aws_cloudwatch_event_rule.create_schedule_in_eb_event_sub_rule.name
event_bus_name = local.computed_sch_operations_bus_name
}
//allow eventbridge to call lambda
resource "aws_lambda_permission" "allow_eventbridge_to_call_lambda" {
statement_id = "AllowExecutionFromCloudWatch"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.create_schedule_in_eb_lambda_function.function_name
principal = "events.amazonaws.com"
source_arn = aws_cloudwatch_event_rule.create_schedule_in_eb_event_sub_rule.arn
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment