diff --git a/exports.js b/exports.js index dcbf2a3dbb..7d332109bc 100644 --- a/exports.js +++ b/exports.js @@ -544,6 +544,7 @@ module.exports = { 'mqBrokerPublicAccess' : require(__dirname + '/plugins/aws/mq/mqBrokerPublicAccess.js'), 'memorydbClusterEncrypted' : require(__dirname + '/plugins/aws/memorydb/memorydbClusterEncrypted.js'), + 'memorydbSnapshotRetention' : require(__dirname + '/plugins/aws/memorydb/memorydbSnapshotRetention.js'), 'mskClusterCBEncryption' : require(__dirname + '/plugins/aws/msk/mskClusterCBEncryption.js'), diff --git a/plugins/aws/memorydb/memorydbSnapshotRetention.js b/plugins/aws/memorydb/memorydbSnapshotRetention.js new file mode 100644 index 0000000000..f09deea26e --- /dev/null +++ b/plugins/aws/memorydb/memorydbSnapshotRetention.js @@ -0,0 +1,57 @@ +var async = require('async'); +var helpers = require('../../../helpers/aws'); + +module.exports = { + title: 'MemoryDB Cluster Automated Snapshot Retention Period', + category: 'MemoryDB', + domain: 'Databases', + severity: 'Low', + description: 'Ensures that retention period is set for Amazon MemoryDB cluster automated snapshots.', + more_info: 'MemoryDB clusters should have retention period set for automated snapshots for data protection and to avoid unexpected failures.', + recommended_action: 'Modify MemoryDB cluster and enable automatic snapshots', + link: 'https://docs.aws.amazon.com/memorydb/latest/devguide/snapshots-automatic.html', + apis: ['MemoryDB:describeClusters'], + realtime_triggers: ['MemoryDB:CreateCluster', 'MemoryDB:DeleteCluster'], + + run: function(cache, settings, callback) { + var results = []; + var source = {}; + var regions = helpers.regions(settings); + + async.each(regions.memorydb, function(region, rcb){ + var describeClusters = helpers.addSource(cache, source, + ['memorydb', 'describeClusters', region]); + + if (!describeClusters) return rcb(); + + if (describeClusters.err || !describeClusters.data) { + helpers.addResult(results, 3, + `Unable to list MemoryDB clusters: ${helpers.addError(describeClusters)}`, region); + return rcb(); + } + + if (!describeClusters.data.length) { + helpers.addResult(results, 0, + 'No MemoryDB clusters found', region); + return rcb(); + } + + for (let cluster of describeClusters.data) { + if (!cluster.ARN) continue; + + let resource = cluster.ARN; + + if (cluster.SnapshotRetentionLimit && cluster.SnapshotRetentionLimit > 0) { + + helpers.addResult(results, 0, 'MemoryDB cluster has snapshot retention period set', region, resource); + } else { + helpers.addResult(results, 2, 'MemoryDB cluster does not have snapshot retention period set', region, resource); + } + } + + rcb(); + }, function(){ + callback(null, results, source); + }); + } +}; \ No newline at end of file diff --git a/plugins/aws/memorydb/memorydbSnapshotRetention.spec.js b/plugins/aws/memorydb/memorydbSnapshotRetention.spec.js new file mode 100644 index 0000000000..8c9aff4c8d --- /dev/null +++ b/plugins/aws/memorydb/memorydbSnapshotRetention.spec.js @@ -0,0 +1,91 @@ +var expect = require('chai').expect; +var memorydbSnapshotRetention = require('./memorydbSnapshotRetention'); + +const describeClusters = [ + { + "Name": "aquacluster", + "Status": "creating", + "NumberOfShards": 1, + "SubnetGroupName": "subnet1", + "TLSEnabled": true, + "ARN": "arn:aws:memorydb:us-east-1:000111222333:cluster/aquacluster", + "SnapshotRetentionLimit": 1, + "MaintenanceWindow": "wed:08:00-wed:09:00", + "SnapshotWindow": "06:30-07:30", + "ACLName": "open-access", + "AutoMinorVersionUpgrade": true + }, + { + "Name": "aquacluster", + "Status": "available", + "NumberOfShards": 1, + "ARN": "arn:aws:memorydb:us-east-1:000111222333:cluster/aquacluster", + "SnapshotRetentionLimit": 0, + "MaintenanceWindow": "tue:06:00-tue:07:00", + "SnapshotWindow": "04:00-05:00", + "ACLName": "open-access", + "AutoMinorVersionUpgrade": true + } +]; + +const createCache = (clusters, logGroupErr) => { + return { + memorydb: { + describeClusters: { + 'us-east-1': { + err: logGroupErr, + data: clusters + }, + }, + }, + }; +}; + + +describe('memorydbSnapshotRetention', function () { + describe('run', function () { + it('should PASS if MemoryDB Cluster for Redis has snapshot retention period set', function (done) { + const cache = createCache([describeClusters[0]]); + memorydbSnapshotRetention.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('MemoryDB cluster has snapshot retention period set'); + expect(results[0].region).to.equal('us-east-1'); + done(); + }); + }); + + it('should FAIL if MemoryDB Cluster for Redis does not have snapshot retention period set', function (done) { + const cache = createCache([describeClusters[1]]); + memorydbSnapshotRetention.run(cache, {} , (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include('MemoryDB cluster does not have snapshot retention period set'); + expect(results[0].region).to.equal('us-east-1'); + done(); + }); + }); + + it('should PASS if no No MemoryDB Cluster found', function (done) { + const cache = createCache([]); + memorydbSnapshotRetention.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('No MemoryDB clusters found'); + expect(results[0].region).to.equal('us-east-1'); + done(); + }); + }); + + it('should UNKNOWN if unable to list MemoryDB Clusters', function (done) { + const cache = createCache(null, { message: "Unable to list MemoryDB clusters" }); + memorydbSnapshotRetention.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(3); + expect(results[0].region).to.equal('us-east-1'); + done(); + }); + }); + + }); +});