Skip to content

Commit 007346d

Browse files
authored
Merge pull request #37 from SumoLogic/cloudwatchevents
SUMO-0: first check in of cloudwatch event function
2 parents c5ad757 + 08bf03f commit 007346d

File tree

2 files changed

+156
-0
lines changed

2 files changed

+156
-0
lines changed

cloudwatchevents/README.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Sumo Logic Function for AWS CloudWatch Events
2+
3+
AWS Lambda function to collect CloudWatch events and post them to [SumoLogic](http://www.sumologic.com) via a [HTTP collector endpoint](http://help.sumologic.com/Send_Data/Sources/02Sources_for_Hosted_Collectors/HTTP_Source)
4+
5+
6+
# Usage
7+
8+
First create an [HTTP collector endpoint](http://help.sumologic.com/Send_Data/Sources/02Sources_for_Hosted_Collectors/HTTP_Source) within SumoLogic. You will need the endpoint URL for the lambda function later.
9+
10+
## Create Lambda Function
11+
12+
1. Within the AWS Lambda console select create new Lambda function
13+
2. Select `Blank Function` on the select blueprint page
14+
3. Leave triggers empty for now, click next
15+
4. Configure Lambda
16+
* Select Node.js 4.3 as runtime
17+
* Copy code from cloudwatchevents.js into the Lambda function code.
18+
* Add Environment variables (See below)
19+
5. Scroll down to the `Lambda function handle and role` section, make sure you set the right values that match the function. For role, you can just use the basic execution role. Click next.
20+
6. Finally click on "Create function" to create the function.
21+
7. (Optional) Test this new function with sample AWS CloudWatch Events template provided by AWS
22+
23+
24+
# Lambda Environment Variables
25+
26+
The following AWS Lambda environment variables are supported
27+
28+
* `SUMO_ENDPOINT` (REQUIRED) - SumoLogic HTTP Collector [endpoint URL](http://help.sumologic.com/Send_Data/Sources/02Sources_for_Hosted_Collectors/HTTP_Source).
29+
* `SOURCE_CATEGORY_OVERRIDE` (OPTIONAL) - Override _sourceCategory metadata field within SumoLogic. If `none` will not be overridden
30+
* `SOURCE_HOST_OVERRIDE` (OPTIONAL) - Override _sourceHost metadata field within SumoLogic. If `none` will not be overridden
31+
* `SOURCE_NAME_OVERRIDE` (OPTIONAL) - Override _sourceName metadata field within SumoLogic. If `none` will not be overridden
32+
33+
# Excluding Outer Event Fields
34+
By default, a CloudWatch Event has a format similar to this:
35+
36+
```
37+
{
38+
"version":"0",
39+
"id":"0123456d-7e46-ecb4-f5a2-e59cec50b100",
40+
"detail-type":"AWS API Call via CloudTrail",
41+
"source":"aws.logs",
42+
"account":"012345678908",
43+
"time":"2017-11-06T23:36:59Z",
44+
"region":"us-east-1",
45+
"resources":[ ],
46+
"detail":▶{ … }
47+
}
48+
```
49+
50+
This event will be sent as-is to Sumo Logic. If you just want to send the ```detail``` key instead, set the ```removeOuterFields``` variable to true.
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2+
// CloudWatch Events to SumoLogic //
3+
// https://github.com/SumoLogic/sumologic-aws-lambda/tree/master/cloudwatchevents //
4+
// //
5+
// YOU MUST CREATE A SUMO LOGIC ENDPOINT CALLED SUMO_ENDPOINT AND PASTE IN ENVIRONMENTAL VARIABLES BELOW //
6+
// https://help.sumologic.com/Send_Data/Sources/02Sources_for_Hosted_Collectors/HTTP_Source //
7+
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
8+
9+
// SumoLogic Endpoint to post logs
10+
var SumoURL = process.env.SUMO_ENDPOINT;
11+
12+
// For some beta AWS services, the default is to remove the outer fields of the received object since they are not useful.
13+
// change this if necessary.
14+
var removeOuterFields = false;
15+
16+
// The following parameters override the sourceCategoryOverride, sourceHostOverride and sourceNameOverride metadata fields within SumoLogic.
17+
// Not these can also be overridden via json within the message payload. See the README for more information.
18+
var sourceCategoryOverride = process.env.SOURCE_CATEGORY_OVERRIDE || ''; // If empty sourceCategoryOverride will not be overridden
19+
var sourceHostOverride = process.env.SOURCE_HOST_OVERRIDE || ''; // If empty sourceHostOverride will not be set to the name of the logGroup
20+
var sourceNameOverride = process.env.SOURCE_NAME_OVERRIDE || ''; // If empty sourceNameOverride will not be set to the name of the logStream
21+
22+
var https = require('https');
23+
var zlib = require('zlib');
24+
var url = require('url');
25+
26+
27+
function postToSumo(context, messages) {
28+
var messagesTotal = Object.keys(messages).length;
29+
var messagesSent = 0;
30+
var messageErrors = [];
31+
32+
var urlObject = url.parse(SumoURL);
33+
var options = {
34+
'hostname': urlObject.hostname,
35+
'path': urlObject.pathname,
36+
'method': 'POST'
37+
};
38+
39+
var finalizeContext = function () {
40+
var total = messagesSent + messageErrors.length;
41+
if (total == messagesTotal) {
42+
console.log('messagesSent: ' + messagesSent + ' messagesErrors: ' + messageErrors.length);
43+
if (messageErrors.length > 0) {
44+
context.fail('errors: ' + messageErrors);
45+
} else {
46+
context.succeed();
47+
}
48+
}
49+
};
50+
51+
52+
Object.keys(messages).forEach(function (key, index) {
53+
var headerArray = key.split(':');
54+
options.headers = {
55+
'X-Sumo-Name': headerArray[0],
56+
'X-Sumo-Category': headerArray[1],
57+
'X-Sumo-Host': headerArray[2]
58+
};
59+
60+
var req = https.request(options, function (res) {
61+
res.setEncoding('utf8');
62+
res.on('data', function (chunk) {});
63+
res.on('end', function () {
64+
console.log("Got response code: "+ res.statusCode);
65+
if (res.statusCode == 200) {
66+
messagesSent++;
67+
} else {
68+
messageErrors.push('HTTP Return code ' + res.statusCode);
69+
}
70+
finalizeContext();
71+
});
72+
});
73+
74+
req.on('error', function (e) {
75+
messageErrors.push(e.message);
76+
finalizeContext();
77+
});
78+
79+
for (var i = 0; i < messages[key].length; i++) {
80+
req.write(JSON.stringify(messages[key][i]) + '\n');
81+
}
82+
req.end();
83+
});
84+
}
85+
86+
87+
exports.handler = function (event, context) {
88+
89+
// Used to hold chunks of messages to post to SumoLogic
90+
var messageList = {};
91+
92+
// Validate URL has been set
93+
var urlObject = url.parse(SumoURL);
94+
if (urlObject.protocol != 'https:' || urlObject.host === null || urlObject.path === null) {
95+
context.fail('Invalid SUMO_ENDPOINT environment variable: ' + SumoURL);
96+
}
97+
98+
console.log(event);
99+
if ((event.source==="aws.guardduty") && (removeOuterFields)) {
100+
final_event =event.detail;
101+
} else {
102+
final_event = event;
103+
}
104+
messageList[sourceNameOverride+':'+sourceCategoryOverride+':'+sourceHostOverride]=[final_event];
105+
postToSumo(context, messageList);
106+
};

0 commit comments

Comments
 (0)