329 lines
12 KiB
JavaScript
329 lines
12 KiB
JavaScript
const computeManagementClient = require("@azure/arm-compute").ComputeManagementClient;
|
|
const dnsManagementClient = require("@azure/arm-dns").DnsManagementClient;
|
|
const ManagedIdentityCredential = require("@azure/identity").ManagedIdentityCredential;
|
|
const storageManagementClient = require("@azure/arm-storage").StorageManagementClient;
|
|
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 _getStorageAccountClient(scenarioName) {
|
|
var id = SUBSCRIPTION_ID;
|
|
if (scenarioName){
|
|
var scenario = await db.scenario.getOne({"name": scenarioName});
|
|
id = scenario.subscription? scenario.subscription.subsId : SUBSCRIPTION_ID;
|
|
}
|
|
const credential = new ManagedIdentityCredential();
|
|
//console.log("AzureCLI# authenticated", credential, id);
|
|
return new storageManagementClient(credential, id);
|
|
}
|
|
|
|
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;
|
|
}
|
|
const credential = new ManagedIdentityCredential();
|
|
//console.log("AzureCLI# authenticated", credential, id);
|
|
return new computeManagementClient(credential, 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;
|
|
}
|
|
const credential = new ManagedIdentityCredential();
|
|
//console.log("AzureCLI# authenticated", credential, id);
|
|
return new dnsManagementClient(credentials, id);
|
|
}
|
|
|
|
|
|
|
|
async function deallocate(provision, triggerUserId, 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);
|
|
|
|
db.provision.update(provision._id, {"statusVms": "Stopping"});
|
|
|
|
for await (const vm of finalResult) {
|
|
await computeClient.virtualMachines.beginDeallocateAndWait(rgName, vm.name);
|
|
}
|
|
|
|
await utils.afterStopVms(provision, triggerUserId, 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, triggerUserId){
|
|
|
|
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);
|
|
|
|
db.provision.update(provision._id, {"statusVms": "Starting"});
|
|
|
|
for await (const vm of finalResult) {
|
|
await computeClient.virtualMachines.beginStartAndWait(rgName, vm.name);
|
|
}
|
|
|
|
await utils.afterStartVms( provision, triggerUserId );
|
|
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 ) {
|
|
|
|
for await (const vm of finalResult) {
|
|
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.beginUpdateAndWait(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 );
|
|
}
|
|
|
|
async function createSnapshots(provId, targetRg = "QMI-Machines", userId) {
|
|
|
|
const provision = await db.provision.getById(provId);
|
|
|
|
const computeClient = await _getClient(provision.scenario);
|
|
const resourceGroupName = _getRgName(provision);
|
|
console.log(`AzureCLI# Start creating snapshot for RG: ${resourceGroupName}`);
|
|
const disks = await computeClient.disks.listByResourceGroup(resourceGroupName);
|
|
const now = Date.now();
|
|
//console.log("AzureCLI# DISKS", disks);
|
|
|
|
|
|
try {
|
|
var out = [];
|
|
|
|
const disks = computeClient.disks.listByResourceGroup(resourceGroupName);
|
|
|
|
for await (const disk of disks) {
|
|
//await processItems(disks, async function(disk){
|
|
console.log(`AzureCLI# Start creating snapshot for disk: ${disk.name}: ${disk.id}`);
|
|
|
|
// Define snapshot parameters
|
|
let snapshotParams = {
|
|
location: disk.location, // Example: "eastus"
|
|
creationData: {
|
|
createOption: "Copy", // Creates a snapshot from an existing disk
|
|
sourceResourceId: `/subscriptions/${SUBSCRIPTION_ID}/resourceGroups/${resourceGroupName}/providers/Microsoft.Compute/disks/${disk.name}`
|
|
}
|
|
};
|
|
|
|
let snapshotName = `Snap_${disk.name}_${provision._id}_${now}`
|
|
console.log(`AzureCLI# creating.... ${snapshotName}`);
|
|
let snapshot = await computeClient.snapshots.beginCreateOrUpdateAndWait(
|
|
targetRg,
|
|
snapshotName,
|
|
snapshotParams
|
|
);
|
|
await db.snapshot.add({"resourceID": snapshot.id, "provision": provision._id, "owner": userId, targetRg: targetRg, name: snapshotName });
|
|
out.push(snapshot);
|
|
|
|
console.log(`AzureCLI# snapshot: ${snapshotName} is DONE!`);
|
|
};
|
|
|
|
return out;
|
|
|
|
|
|
} catch (error) {
|
|
console.log("AzureCLI# Error creating snapshot:", error);
|
|
return null;
|
|
}
|
|
|
|
}
|
|
|
|
async function rotateStorageAccountKey(provision, keyName = "key1") {
|
|
|
|
if ( !provision || !provision.outputs) {
|
|
console.log("AzureCLI# rotateStorageAccountKey - No Proviosn or no outputs");
|
|
return null;
|
|
}
|
|
|
|
if (provision.outputs["StorageAccount-Name"] && provision.outputs["StorageAccount-AccessKey"]){
|
|
const storageAccountName = provision.outputs["StorageAccount-Name"];
|
|
try {
|
|
const client = await _getStorageAccountClient(provision.scenario);
|
|
const resourceGroupName = _getRgName(provision);
|
|
|
|
console.log(`AzureCLI# rotateStorageAccountKey - Regenerating ${keyName} for ${storageAccountName}...`);
|
|
|
|
const result = await client.storageAccounts.regenerateKey(resourceGroupName, storageAccountName, {
|
|
keyName: keyName
|
|
});
|
|
let newKey = result.keys.find(k => k.keyName === keyName).value;
|
|
console.log("AzureCLI# rotateStorageAccountKey - New Key:", storageAccountName, newKey);
|
|
let outputs = provision.outputs;
|
|
outputs["StorageAccount-AccessKey"] = newKey;
|
|
outputs["StorageAccount-ConnectionString"] = `DefaultEndpointsProtocol=https;AccountName=${storageAccountName};AccountKey=${newKey};EndpointSuffix=core.windows.net`;
|
|
const upProv = await db.provision.update(provision._id, {"outputs": outputs});
|
|
|
|
console.log(`AzureCLI# Done rotateStorageAccountKey!! - ${keyName} for ${storageAccountName}...`);
|
|
|
|
return upProv;
|
|
} catch (error) {
|
|
console.log("AzureCLI# Error rotateStorageAccountKey:", error);
|
|
return null;
|
|
}
|
|
} else {
|
|
console.log(`AzureCLI# rotateStorageAccountKey -Provision (${provision._id}) Outputs does not contain a storage account`);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
|
|
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;
|
|
module.exports.createSnapshots = createSnapshots;
|
|
module.exports.rotateStorageAccountKey = rotateStorageAccountKey;
|