Skip to content

Instantly share code, notes, and snippets.

@singledigit
Last active June 22, 2018 21:43
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 singledigit/2754012d34440102c043935926cf10fc to your computer and use it in GitHub Desktop.
Save singledigit/2754012d34440102c043935926cf10fc to your computer and use it in GitHub Desktop.
RDS Snapshot Lambda for automating manual backups.
const AWS = require('aws-sdk');
const rds = new AWS.RDS();
const sns = new AWS.SNS();
const prefix = `snapper-${process.env.TIME_TO_LIVE}-${process.env.TIME_TO_LIVE_METRIC}-${process.env.CLUSTER_ID}`
const createClusterSnapshot = () => {
let params = {
DBClusterIdentifier: process.env.CLUSTER_ID,
DBClusterSnapshotIdentifier: `${prefix}-${Date.now()}`,
Tags: [{ Key: 'type', Value: 'snapper' }]
}
return rds.createDBClusterSnapshot(params).promise();
}
const success = (data) => {
console.log(`Snapshot and Pruning COMPLETED for ${prefix}`);
console.log(data);
if (process.env.BRODCAST_ON_SUCCESS) {
let snsParams = {
Subject: `Snapshot and Pruning COMPLETED for ${prefix}`,
Message: JSON.stringify(data),
TopicArn: process.env.SNS_TOPIC_ARN
}
return sns.publish(snsParams).promise()
}
else return Promise.resolve(data)
}
const errorHandler = (err) => {
console.log(`Snapshot and Pruning FAILED for ${prefix}`);
console.log(err);
let snsParams = {
Subject: `Snapshot and Pruning FAILED for ${prefix}`,
Message: JSON.stringify(err)
}
return sns.publish(snsParams).promise()
}
const getSnapshots = () => {
let params = {
DBClusterIdentifier: process.env.CLUSTER_ID,
SnapshotType: 'manual'
}
return rds.describeDBClusterSnapshots(params).promise();
}
const pruneSnapshots = (snapshots) => {
return snapshots.map(snap => {
if (snap.DBClusterSnapshotIdentifier.toLowerCase().startsWith(prefix.toLowerCase())) {
let createdDate = new Date(snap.SnapshotCreateTime);
let oldestDate = new Date();
let deleteSnap = false;
switch (process.env.TIME_TO_LIVE_METRIC.toLowerCase()) {
case 'second':
deleteSnap = createdDate < (oldestDate.setSeconds(oldestDate.getSeconds() - process.env.TIME_TO_LIVE))
break;
case 'minute':
deleteSnap = createdDate < (oldestDate.setMinutes(oldestDate.getMinutes() - process.env.TIME_TO_LIVE))
break;
case 'hour':
deleteSnap = createdDate < (oldestDate.setHours(oldestDate.getHours() - process.env.TIME_TO_LIVE))
break;
case 'day':
deleteSnap = ((Date.now() - createdDate) / (1000 * 60 * 60 * 24)) > process.env.TIME_TO_LIVE
break;
default:
deleteSnap = false;
break;
}
if (deleteSnap) return rds.deleteDBClusterSnapshot({ DBClusterSnapshotIdentifier: snap.DBClusterSnapshotIdentifier }).promise()
}
})
}
exports.handler = async () => {
try {
// create new snapshot
let newSnapshot = await createClusterSnapshot();
// grab current list of snapshots
let snapshots = await getSnapshots();
// create a list of prune requests
let pruneRequests = pruneSnapshots(snapshots.DBClusterSnapshots);
// delete snapshots according to criteria
let deletedSnaps = await Promise.all(pruneRequests)
// success
return success({ "New-Snapshot": newSnapshot, "DeletedSnapshots": deletedSnaps });
}
catch (err) {
errorHandler(err);
}
};
AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: Automated process for keeping manual snapshots on aurora
Parameters:
ClusterId:
Type: String
Description: The name of the Aurora Cluster to be snapped
Globals:
Function:
Runtime: nodejs8.10
MemorySize: 128
Timeout: 300
Environment:
Variables:
SNS_TOPIC_ARN: !Ref SNSBroadcast
CLUSTER_ID: !Ref ClusterId
Resources:
# SNS Topic for notifications
SNSBroadcast:
Type: "AWS::SNS::Topic"
Properties:
DisplayName: AuroraSnapperBroadcast
# Policy to create logs if needed
SnapperManagedLogPolicy:
Type: "AWS::IAM::ManagedPolicy"
Properties:
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: '*'
# Policy to access RDS
SnapperManagedAuroraPolicy:
Type: "AWS::IAM::ManagedPolicy"
Properties:
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- rds:CreateDBClusterSnapshot
- rds:DeleteDBClusterSnapshot
- rds:DescribeDBClusterSnapshots
Resource: '*'
SnapperManagedSQSPolicy:
Type: "AWS::IAM::ManagedPolicy"
Properties:
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- sns:Publish
Resource: !Ref SNSBroadcast
## Duplicate as needed
SnapperLambda24Hour:
Type: 'AWS::Serverless::Function'
Properties:
Policies:
- !Ref SnapperManagedLogPolicy
- !Ref SnapperManagedAuroraPolicy
- !Ref SnapperManagedSQSPolicy
Handler: snapper/index.handler
Events:
CreateAuroraSnapshot:
Type: Schedule
Properties:
## Change frequency variable # Cron syntax works as well
Schedule: rate(1 hour) # rate(30 minutes) | rate(1 hour) | rate(7 day)
Environment:
Variables:
## ## ## ## ## Change Variables below
TIME_TO_LIVE: 24
TIME_TO_LIVE_METRIC: Hour # Second | Minute | Hour | Day | Month
BROADCAST_ON_SUCCESS: false
SnapperLambda7Day:
Type: 'AWS::Serverless::Function'
Properties:
Policies:
- !Ref SnapperManagedLogPolicy
- !Ref SnapperManagedAuroraPolicy
- !Ref SnapperManagedSQSPolicy
Handler: snapper/index.handler
Events:
CreateAuroraSnapshot:
Type: Schedule
Properties:
## Change frequency variable # Cron syntax works as well
Schedule: rate(1 day) # rate(30 minutes) | rate(1 hour) | rate(7 day)
Environment:
Variables:
## ## ## ## ## Change Variables below
TIME_TO_LIVE: 7
TIME_TO_LIVE_METRIC: Day # Second | Minute | Hour | Day | Month
BROADCAST_ON_SUCCESS: true
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment