diff --git a/exports.js b/exports.js index 988ba4986d..0621c1b17a 100644 --- a/exports.js +++ b/exports.js @@ -1223,6 +1223,7 @@ module.exports = { 'workspaceManagedIdentity' : require(__dirname + '/plugins/azure/synapse/workspaceManagedIdentity.js'), 'synapseWorkspaceAdAuthEnabled' : require(__dirname + '/plugins/azure/synapse/synapseWorkspaceAdAuthEnabled.js'), 'synapseWorkspacPrivateEndpoint': require(__dirname + '/plugins/azure/synapse/synapseWorkspacPrivateEndpoint.js'), + 'synapseWorkspaceHasTags' : require(__dirname + '/plugins/azure/synapse/synapseWorkspaceHasTags.js'), 'workspaceDiagnosticLogsEnabled': require(__dirname + '/plugins/azure/synapse/workspaceDiagnosticLogsEnabled.js'), 'workspaceDoubleEncryption' : require(__dirname + '/plugins/azure/synapse/workspaceDoubleEncryption.js'), diff --git a/plugins/azure/synapse/synapseWorkspaceHasTags.js b/plugins/azure/synapse/synapseWorkspaceHasTags.js new file mode 100644 index 0000000000..05f4053752 --- /dev/null +++ b/plugins/azure/synapse/synapseWorkspaceHasTags.js @@ -0,0 +1,54 @@ +var async = require('async'); +var helpers = require('../../../helpers/azure'); + +module.exports = { + title: 'Synapse Workspace Has Tags', + category: 'AI & ML', + domain: 'Machine Learning', + severity: 'Low', + description: 'Ensures that Azure Synapse workspaces 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 Synapse workspace and add tags.', + link: 'https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources', + apis: ['synapse:listWorkspaces'], + realtime_triggers: ['microsoftsynapse:workspaces:write','microsoftsynapse:workspaces:delete','microsoftresources:tags:write'], + + run: function(cache, settings, callback) { + const results = []; + const source = {}; + const locations = helpers.locations(settings.govcloud); + + async.each(locations.synapse, function(location, rcb) { + const workspaces = helpers.addSource(cache, source, + ['synapse', 'listWorkspaces', location]); + + if (!workspaces) return rcb(); + + + if (workspaces.err || !workspaces.data) { + helpers.addResult(results, 3, 'Unable to query Synapse workspaces: ' + helpers.addError(workspaces), location); + return rcb(); + } + + if (!workspaces.data.length) { + helpers.addResult(results, 0, 'No existing Synapse workspaces found', location); + return rcb(); + } + + for (let workspace of workspaces.data) { + if (!workspace.id) continue; + + if (workspace.tags && + Object.entries(workspace.tags).length > 0) { + helpers.addResult(results, 0, 'Synapse workspace has tags', location, workspace.id); + } else { + helpers.addResult(results, 2, 'Synapse workspace does not have tags', location, workspace.id); + } + } + rcb(); + }, function() { + // Global checking goes here + callback(null, results, source); + }); + } +}; \ No newline at end of file diff --git a/plugins/azure/synapse/synapseWorkspaceHasTags.spec.js b/plugins/azure/synapse/synapseWorkspaceHasTags.spec.js new file mode 100644 index 0000000000..2663374e28 --- /dev/null +++ b/plugins/azure/synapse/synapseWorkspaceHasTags.spec.js @@ -0,0 +1,85 @@ +var expect = require('chai').expect; +var synapseWorkspaceHasTags = require('./synapseWorkspaceHasTags'); + +const workspaces = [ + { + type: "Microsoft.Synapse/workspaces", + id: "/subscriptions/123/resourceGroups/rsgrp/providers/Microsoft.Synapse/workspaces/test", + location: "eastus", + name: "test", + "tags": { + "key": "value" + } + }, + { + type: "Microsoft.Synapse/workspaces", + id: "/subscriptions/123/resourceGroups/rsgrp/providers/Microsoft.Synapse/workspaces/test", + location: "eastus", + name: "test", + "tags": {} + }, +]; + + +const createCache = (workspaces, err) => { + + return { + synapse: { + listWorkspaces: { + 'eastus': { + data: workspaces, + err: err + } + } + } + }; +}; + +describe('synapseWorkspaceHasTags', function () { + describe('run', function () { + + it('should give a passing result if no Synapse workspaces are found', function (done) { + const cache = createCache([], null); + synapseWorkspaceHasTags.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 Synapse workspaces found'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give unknown result if unable to query for Synapse workspaces', function (done) { + const cache = createCache(null, ['error']); + synapseWorkspaceHasTags.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 Synapse workspaces'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give passing result if workspace has tags', function (done) { + const cache = createCache([workspaces[0]], null); + synapseWorkspaceHasTags.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(0); + expect(results[0].message).to.include('Synapse workspace has tags'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + + it('should give failing result if workspace does not have tags', function (done) { + const cache = createCache([workspaces[1]], null); + synapseWorkspaceHasTags.run(cache, {}, (err, results) => { + expect(results.length).to.equal(1); + expect(results[0].status).to.equal(2); + expect(results[0].message).to.include('Synapse workspace does not have tags'); + expect(results[0].region).to.equal('eastus'); + done(); + }); + }); + }); +}); \ No newline at end of file