Skip to content

Instantly share code, notes, and snippets.

@seeebiii
Created October 1, 2018 20:52
Show Gist options
  • Save seeebiii/c91815200915a17131c9d908c525c357 to your computer and use it in GitHub Desktop.
Save seeebiii/c91815200915a17131c9d908c525c357 to your computer and use it in GitHub Desktop.
Lambda function to help you having clean CloudWatch log groups

Why

If you don't set a name for your Lambda functions, AWS will choose one for you based on the following pattern: [stack_name]-[function_name]-[random_hash]. The problem is there are certain cases where your Lambda function gets a new name, e.g. if you have to redeploy your stack or if you have renamed the logical resource id of your function. Then, CloudFormation will associate a new CloudWatch log group with your Lambda function. In the end, this means you'll have a lot of unused log groups and you might need some time to figure out which one is the most recent one.

How

Simple use this file as a Lambda function and call it e.g. once a day. Suggestion: Have a separate maintenance stack in your AWS account where you use this function. Then you'll always have a clean set of CloudWatch log groups :-)

Who

Copyright (c) 2018 Sebastian Hesse

const LOGS = require('aws-sdk/clients/cloudwatchlogs');
const logs = new LOGS();
const STACK_NAME = process.env.STACK_NAME;
const LAMBDA_LOG_GROUP_PREFIX = '/aws/lambda/';
const LAMBDA_RESOURCE_TYPE = 'AWS::Lambda::Function';
/**
* Remove old CloudWatch groups which were part of the stack before, but don't have a related Lambda function anymore.
*/
let removeOldLogGroups = function() {
const lambdaFunctionsSet = new Set();
console.log('Checking CloudWatch log groups...');
cf.describeStackResources({
StackName: STACK_NAME
}).promise().then(res => {
let resources = res.StackResources;
let lambdaFunctions = resources.filter(value => value.ResourceType === LAMBDA_RESOURCE_TYPE);
lambdaFunctions.forEach(value => {
lambdaFunctionsSet.add(value.PhysicalResourceId);
});
console.log('Current set of Lambda functions: ', lambdaFunctionsSet);
removeGroups(lambdaFunctionsSet, null).then(() => {
console.log('Removed all old CloudWatch log groups.');
});
}).catch(err => {
console.log('Error: ', err);
});
};
function removeGroups(lambdaFunctionsSet, nextToken) {
let params = {
logGroupNamePrefix: LAMBDA_LOG_GROUP_PREFIX
};
if (nextToken) {
params.nextToken = nextToken;
}
// we need to retrieve all existing log groups first
return logs.describeLogGroups(params).promise().then(logGroups => {
// => compare each log group name with the current set of Lambda functions and remove unused groups
return Promise.all(removeGroupIfPartOfStack(lambdaFunctionsSet, logGroups)).then(() => {
if (logGroups.nextToken) {
return removeGroups(lambdaFunctionsSet, logGroups.nextToken);
}
});
});
}
function removeGroupIfPartOfStack(lambdaFunctionsSet, logGroups) {
let promises = [];
// go through all log groups and decide if we need to remove them
logGroups.logGroups.forEach(logGroup => {
let logGroupName = logGroup.logGroupName;
let lambdaName = logGroupName.replace(LAMBDA_LOG_GROUP_PREFIX, '');
// if a Lambda function does not have a fixed name, AWS set it's automatically with this pattern:
// [stack_name]-[function_name]-[random_hash]
// hence, check if this log group was somehow related to a Lambda function from the stack
// => if not and it's not used for a current Lambda function, simply remove it
if (lambdaName && lambdaName.indexOf(STACK_NAME) > -1 && !lambdaFunctionsSet.has(lambdaName)) {
promises.push(logs.deleteLogGroup({
logGroupName: logGroupName
}).promise().then(() => {
console.log('Removed log group for Lambda: ', logGroupName);
}).catch(err => {
console.log('Failed to remove log group for Lanbda: ', logGroupName);
}));
}
});
return promises;
}
module.exports = {
removeOldLogGroups
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment