diff --git a/exports.js b/exports.js index 9e17cc88e6..e8333cda24 100644 --- a/exports.js +++ b/exports.js @@ -859,6 +859,7 @@ module.exports = { 'resourceAllowedLocations' : require(__dirname + '/plugins/azure/policyservice/resourceAllowedLocations.js'), 'resourceLocationMatch' : require(__dirname + '/plugins/azure/policyservice/resourceLocationMatch.js'), + 'mysqlFlexibleServerHasTags' : require(__dirname + '/plugins/azure/mysqlserver/mysqlFlexibleServerHasTags.js'), 'enforceMySQLSSLConnection' : require(__dirname + '/plugins/azure/mysqlserver/enforceMySQLSSLConnection.js'), 'mysqlFlexibleServersMinTls' : require(__dirname + '/plugins/azure/mysqlserver/mysqlFlexibleServersMinTls.js'), 'mysqlFlexibleServerVersion' : require(__dirname + '/plugins/azure/mysqlserver/mysqlFlexibleServerVersion.js'), diff --git a/plugins/azure/mysqlserver/mysqlFlexibleServerHasTags.js b/plugins/azure/mysqlserver/mysqlFlexibleServerHasTags.js new file mode 100644 index 0000000000..efe9ac4425 --- /dev/null +++ b/plugins/azure/mysqlserver/mysqlFlexibleServerHasTags.js @@ -0,0 +1,54 @@ +const async = require('async'); +const helpers = require('../../../helpers/azure'); + +module.exports = { + title: 'MySQL Flexible Server Has Tags', + category: 'MySQL Server', + domain: 'Databases', + severity: 'Low', + description: 'Ensure that Azure MySQL flexible servers have tags associated.', + more_info: 'Tags help you to group resources together that are related to or associated with each other. It is a best practice to tag cloud resources to better organize and gain visibility into their usage.', + recommended_action: 'Modify MySQL flexible server and add tags.', + link: 'https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources', + apis: ['servers:listMysql', 'servers:listMysqlFlexibleServer'], + realtime_triggers: ['microsoftdbformysql:flexibleservers:write','microsoftdbformysql:flexibleservers:delete', 'microsoftresources:tags:write'], + + + run: function(cache, settings, callback) { + const results = []; + const source = {}; + const locations = helpers.locations(settings.govcloud); + + async.each(locations.servers, (location, rcb) => { + const servers = helpers.addSource(cache, source, + ['servers', 'listMysqlFlexibleServer', location]); + + if (!servers) return rcb(); + + if (servers.err || !servers.data) { + helpers.addResult(results, 3, + 'Unable to query for MySQL flexible servers: ' + helpers.addError(servers), location); + return rcb(); + } + + if (!servers.data.length) { + helpers.addResult(results, 0, 'No existing MySQL flexible servers found', location); + return rcb(); + } + + for (var flexibleServer of servers.data) { + if (!flexibleServer.id) continue; + + if (flexibleServer.tags && Object.entries(flexibleServer.tags).length > 0){ + helpers.addResult(results, 0, 'MySQL flexible server has tags associated', location, flexibleServer.id); + } else { + helpers.addResult(results, 2, 'MySQL flexible server does not have tags associated', location, flexibleServer.id); + } + } + + rcb(); + }, function() { + callback(null, results, source); + }); + }, +}; \ No newline at end of file diff --git a/plugins/azure/mysqlserver/mysqlFlexibleServerHasTags.spec.js b/plugins/azure/mysqlserver/mysqlFlexibleServerHasTags.spec.js new file mode 100644 index 0000000000..63fe4a997e --- /dev/null +++ b/plugins/azure/mysqlserver/mysqlFlexibleServerHasTags.spec.js @@ -0,0 +1,87 @@ +var expect = require('chai').expect; +var server = require('./mysqlFlexibleServerHasTags'); + +const servers = [ + { + "id": "/subscriptions/12345/resourceGroups/Default/providers/Microsoft.DBforMySQL/flexibleServers/test-server", + "type": "Microsoft.DBforMySQL/flexibleServers", + "name": 'test-server', + "tags": {"key": "value"}, + }, + { + "id": "/subscriptions/12345/resourceGroups/Default/providers/Microsoft.DBforMySQL/flexibleServers/test-server", + "type": "Microsoft.DBforMySQL/flexibleServers", + "name": 'test-server', + "tags": {}, + } +]; + +const createCache = (server) => { + return { + servers: { + listMysqlFlexibleServer: { + 'eastus': { + data: server + } + } + } + }; +}; + +const createErrorCache = () => { + return { + servers: { + listMysqlFlexibleServer: { + 'eastus': {} + } + } + }; +}; + +describe('mysqlServerHasTags', function() { + describe('run', function() { + it('should give passing result if no servers found', function(done) { + const cache = createCache([]); + server.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('No existing MySQL flexible servers found'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give failing result if MySQL Server does not have tags', function(done) { + const cache = createCache([servers[1]]); + server.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include('MySQL flexible server does not have tags associated'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give unknown result if unable to query for MySQL servers', function(done) { + const cache = createErrorCache(); + server.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(3); + expect(results[0].message).to.include('Unable to query for MySQL flexible servers:'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give passing result if MySQL Server has tags', function(done) { + const cache = createCache([servers[0]]); + server.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('MySQL flexible server has tags associated'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + }); +}); \ No newline at end of file