const loginWithVmMSI = require("@azure/ms-rest-nodeauth").loginWithVmMSI; const computeManagementClient = require("@azure/arm-compute").ComputeManagementClient; const dnsManagementClient = require("@azure/arm-dns").DnsManagementClient; const db = require("./mongo"); const utils = require("./utils"); const SUBSCRIPTION_ID = "62ebff8f-c40b-41be-9239-252d6c0c8ad9"; function _getRgName(provision) { let rgName = provision.scenario.toUpperCase(); rgName = rgName.replace(/AZQMI/g, 'QMI'); rgName = rgName + "-" + provision._id.toString(); return rgName; } async function _getClient(scenarioName) { var id = SUBSCRIPTION_ID; if (scenarioName){ var scenario = await db.scenario.getOne({"name": scenarioName}); id = scenario.subscription? scenario.subscription.subsId : SUBSCRIPTION_ID; } var credentials = await loginWithVmMSI({ port: 50342 }); //console.log("AzureCLI# authenticated", credentials); return new computeManagementClient(credentials, id); } async function _getDNSClient(scenarioName) { var id = SUBSCRIPTION_ID; if (scenarioName){ var scenario = await db.scenario.getOne({"name": scenarioName}); id = scenario.subscription? scenario.subscription.subsId : SUBSCRIPTION_ID; } var credentials = await loginWithVmMSI({ port: 50342 }); //console.log("AzureCLI# authenticated", credentials); return new dnsManagementClient(credentials, id); } async function asyncForEach(array, callback) { for (let index = 0; index < array.length; index++) { await callback(array[index], index, array); } } async function deallocate(provision, isSendEmailAfter ) { let rgName = _getRgName(provision); console.log("AzureCLI# Deallocating VMs for resource group: "+rgName); try { var computeClient = await _getClient(provision.scenario); let finalResult = await computeClient.virtualMachines.list(rgName); if ( finalResult && finalResult.length > 0 ) { db.provision.update(provision._id, {"statusVms": "Stopping"}); } await asyncForEach(finalResult, async function(vm) { await computeClient.virtualMachines.deallocate(rgName, vm.name); }); await utils.afterStopVms(provision, isSendEmailAfter); console.log("AzureCLI# All VMs DEALLOCATED for resource group: "+rgName); } catch ( error ) { console.log("AzureCLI# ERROR stopping VMs: "+rgName, error); await db.provision.update(provision._id.toString(), {"statusVms": "Running" }); } } async function start(provision){ let rgName = _getRgName(provision); console.log("AzureCLI# Starting VMs for resource group: "+rgName); try { var computeClient = await _getClient(provision.scenario); let finalResult = await computeClient.virtualMachines.list(rgName); if ( finalResult && finalResult.length > 0 ) { db.provision.update(provision._id, {"statusVms": "Starting"}); } await asyncForEach(finalResult, async function(vm) { await computeClient.virtualMachines.start(rgName, vm.name); }); await utils.afterStartVms( provision ); console.log("AzureCLI# All VMs RUNNING for resource group: "+rgName); } catch ( error ) { console.log("AzureCLI# ERROR starting VMs: "+rgName, error); await db.provision.update(provision._id.toString(), {"statusVms": "Stopped" }); } } async function getResourceGroupVms(rgName){ let computeClient = await _getClient(); return await computeClient.virtualMachines.list(rgName); } async function getAllVms(){ let computeClient = await _getClient(); console.log("AzureCLI# Retrieving all VMS from subscription"); return await computeClient.virtualMachines.listAll(); } async function getAllVmsNext(nextLink){ let computeClient = await _getClient(); console.log("AzureCLI# Retrieving all VMS from subscription (next): " +nextLink); return await computeClient.virtualMachines.listAllNext(nextLink) } async function updateVmsTags(provId, tagsEdit) { if ( !tagsEdit ){ return {} ; } let provision = await db.provision.getById(provId); if ( !provision ){ return }; let rgName = _getRgName(provision); console.log("AzureCLI# Updating TAGS in VMs for resource group: "+rgName); var computeClient; var finalResult; try { computeClient = await _getClient(provision.scenario); finalResult = await computeClient.virtualMachines.list(rgName); } catch (e2) { console.log(`AzureCLI# ERROR: Resource group '${rgName}' could not be found`, e2); return {}; } if (finalResult && finalResult.length > 0) { var toDelete = []; var toAdd = []; for(let tag in tagsEdit ) { if ( !tagsEdit[tag] ) { toDelete.push(tag); } else { toAdd.push({"key": tag, "value": tagsEdit[tag]}); } } var result = {}; if ( toDelete.length > 0 || toAdd.length > 0 ) { await asyncForEach(finalResult, async function(vm) { var vmActualTags = JSON.stringify(vm.tags); var tags = vm.tags; toDelete.forEach(t=>{ delete tags[t]; }); toAdd.forEach(t=>{ tags[t.key] = t.value; }); tags["ProvId"] = provision._id.toString(); result[vm.name] = tags; if ( JSON.stringify(tags) !== vmActualTags ) { try { console.log(`AzureCLI# setting new tags to VM ${vm.name} (${rgName})`); await computeClient.virtualMachines.update(rgName, vm.name, {"tags": tags} ); } catch (e1) { console.log(`AzureCLI# ERROR setting tags to VM ${vm.name} (${rgName})`, e1); } } else { console.log(`AzureCLI# Same tags, no need to update tags for VM ${vm.name} (${rgName})`); } }); } //db.event.add({ user: provision.user._id, provision: provision._id, type: 'provision.vms-tags-update' }); return result; } else { return {} } }; /** * DNS Records */ const DNS_RESOURCE_GROUP = "QMI-Infra-DNS"; const DNS_ZONE_NAME = "qmi.qlik-poc.com"; async function getDNSRecords(type = "CNAME") { let dnsClient = await _getDNSClient(); console.log(`AzureCLI# Retrieving '${type}' DNS records`); return await dnsClient.recordSets.listByType(DNS_RESOURCE_GROUP, DNS_ZONE_NAME, type); } async function createDNSRecord(provision, cname, type = "CNAME"){ if ( !provision ) return; let rgName = _getRgName(provision).toLowerCase(); let dnsClient = await _getDNSClient(); console.log(`AzureCLI# Creating '${type}' DNS record: ${rgName}.${DNS_ZONE_NAME} --> ${cname}`); let parameters = { "cnameRecord": { "cname": cname }, "tTL": 60 }; return await dnsClient.recordSets.createOrUpdate(DNS_RESOURCE_GROUP, DNS_ZONE_NAME, rgName, type, parameters ); } async function deleteDNSRecord(provision, type = "CNAME"){ if ( !provision ) return; let rgName = _getRgName(provision).toLowerCase(); let dnsClient = await _getDNSClient(); console.log(`AzureCLI# Deleting '${type}' DNS record: ${rgName}.${DNS_ZONE_NAME}`); return await dnsClient.recordSets.deleteMethod(DNS_RESOURCE_GROUP, DNS_ZONE_NAME, rgName, type ); } module.exports.start = start; module.exports.deallocate = deallocate; module.exports.getResourceGroupVms = getResourceGroupVms; module.exports.getAllVms = getAllVms; module.exports.getAllVmsNext = getAllVmsNext; module.exports.updateVmsTags = updateVmsTags; module.exports.getDNSRecords = getDNSRecords; module.exports.createDNSRecord = createDNSRecord; module.exports.deleteDNSRecord = deleteDNSRecord