From b8f41ac410560120bfca7d99205de3fe255a9dcd Mon Sep 17 00:00:00 2001 From: jackyalbo Date: Wed, 15 Mar 2017 15:18:42 +0200 Subject: [PATCH] adding ssh support for both linux and windows will support win32-openssh for windows and will add default user to sudoers for linux --- .../azure-v2/compute/client/servers.js | 145 +++++++++++------- lib/pkgcloud/azure-v2/scripts/ssh.ps1 | 8 + lib/pkgcloud/azure-v2/scripts/sudo.sh | 4 + 3 files changed, 98 insertions(+), 59 deletions(-) create mode 100644 lib/pkgcloud/azure-v2/scripts/ssh.ps1 create mode 100644 lib/pkgcloud/azure-v2/scripts/sudo.sh diff --git a/lib/pkgcloud/azure-v2/compute/client/servers.js b/lib/pkgcloud/azure-v2/compute/client/servers.js index 8ad5b2787..ee0e1f71c 100644 --- a/lib/pkgcloud/azure-v2/compute/client/servers.js +++ b/lib/pkgcloud/azure-v2/compute/client/servers.js @@ -27,7 +27,9 @@ function getVersion(callback) { */ function getLimits(callback) { return errs.handle( - errs.create({ message: 'Azure\'s API is not rate limited' }), + errs.create({ + message: 'Azure\'s API is not rate limited' + }), callback ); } @@ -48,9 +50,9 @@ function getServers(callback) { var client = new ComputeManagementClient(self.azure.credentials, self.config.subscriptionId); client.virtualMachines.list(self.config.resourceGroup, function (err, results) { - return err - ? callback(err) - : callback(null, results.map(function (res) { + return err ? + callback(err) : + callback(null, results.map(function (res) { return new self.models.Server(self, res); })); }); @@ -63,7 +65,7 @@ function getServers(callback) { * @param {Function} callback cb(err, serverId). */ function getServer(server, callback) { - var self = this; + var self = this; var serverId = server instanceof self.models.Server ? server.name : server; self.login(function (err) { @@ -73,9 +75,11 @@ function getServer(server, callback) { } var client = new ComputeManagementClient(self.azure.credentials, self.config.subscriptionId); - + // This will ensure returning of instances running status - var options = { expand: 'instanceView' }; + var options = { + expand: 'instanceView' + }; client.virtualMachines.get(self.config.resourceGroup, serverId, options, function (err, result) { if (err) { @@ -83,15 +87,15 @@ function getServer(server, callback) { } // Get public dns url - if (!result.networkProfile || - !result.networkProfile.networkInterfaces || - !result.networkProfile.networkInterfaces.length) { + if (!result.networkProfile || + !result.networkProfile.networkInterfaces || + !result.networkProfile.networkInterfaces.length) { return callback(null, new self.models.Server(self, result)); } var networkInterfaceId = result.networkProfile.networkInterfaces[0].id; var resourceClient = new resourceManagement.ResourceManagementClient(self.azure.credentials, self.config.subscriptionId); - + resourceClient.resources.getById(networkInterfaceId, constants.DEFAULT_API_VERSION, function (err, networkInterface) { if (err) { @@ -99,11 +103,11 @@ function getServer(server, callback) { } if (!networkInterface.properties.ipConfigurations || - !networkInterface.properties.ipConfigurations.length || - !networkInterface.properties.ipConfigurations[0] || - !networkInterface.properties.ipConfigurations[0].properties || - !networkInterface.properties.ipConfigurations[0].properties.publicIPAddress || - !networkInterface.properties.ipConfigurations[0].properties.publicIPAddress.id) { + !networkInterface.properties.ipConfigurations.length || + !networkInterface.properties.ipConfigurations[0] || + !networkInterface.properties.ipConfigurations[0].properties || + !networkInterface.properties.ipConfigurations[0].properties.publicIPAddress || + !networkInterface.properties.ipConfigurations[0].properties.publicIPAddress.id) { return callback(null, new self.models.Server(self, result)); } @@ -143,21 +147,27 @@ function createServer(options, callback) { if (!options.name || !options.username || !options.password) { return errs.handle( - errs.create({ message: 'Please provide a name for the vm, as well as the username and password for login' }), + errs.create({ + message: 'Please provide a name for the vm, as well as the username and password for login' + }), callback ); } if (!options.flavor) { return errs.handle( - errs.create({ message: 'When creating an azure server a flavor or an image need to be supplied' }), + errs.create({ + message: 'When creating an azure server a flavor or an image need to be supplied' + }), callback ); } var adjustVMTemplate = function (template) { - var vmIndex = _.findIndex(template.resources, { 'type': 'Microsoft.Compute/virtualMachines' }); + var vmIndex = _.findIndex(template.resources, { + 'type': 'Microsoft.Compute/virtualMachines' + }); // Adding additional data disks if (options.storageDataDiskNames && options.storageDataDiskNames.length) { @@ -174,11 +184,11 @@ function createServer(options, callback) { }); } - // If this is a windows machine, add an extension that enables remote connection via WinRM + // If this is a windows machine, add an extension that enables ssh connection via Win32-OpenSSH if (options.osType === 'Windows') { template.resources[vmIndex].resources = [{ 'type': 'Microsoft.Compute/virtualMachines/extensions', - 'name': '[concat(variables(\'vmName\'),\'/WinRMCustomScriptExtension\')]', + 'name': '[concat(variables(\'vmName\'),\'/Win32sshExtension\')]', 'apiVersion': constants.DEFAULT_API_VERSION, 'location': '[resourceGroup().location]', 'dependsOn': [ @@ -187,15 +197,30 @@ function createServer(options, callback) { 'properties': { 'publisher': 'Microsoft.Compute', 'type': 'CustomScriptExtension', - 'typeHandlerVersion': '1.4', + 'typeHandlerVersion': '1.8', 'settings': { - 'fileUris': [ - 'https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/201-vm-winrm-windows/ConfigureWinRM.ps1', - 'https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/201-vm-winrm-windows/makecert.exe', - 'https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/201-vm-winrm-windows/winrmconf.cmd' - ], - 'commandToExecute': '[concat(\'powershell -ExecutionPolicy Unrestricted -file ConfigureWinRM.ps1 \',variables(\'hostDNSNameScriptArgument\'))]' - } + 'fileUris': ["https://raw.githubusercontent.com/CatalystCode/pkgcloud/master/lib/pkgcloud/azure-v2/scripts/ssh.ps1"], + 'commandToExecute': `powershell -File ssh.ps1 ${hostname}\\${options.username} ${options.password}` + }, + } + }]; + } else { // linux - make sure the new user is in sudoers - so he can sudo with no password + template.resources[vmIndex].resources = [{ + 'type': 'Microsoft.Compute/virtualMachines/extensions', + 'name': '[concat(variables(\'vmName\'),\'/LinuxSudoExtension\')]', + 'apiVersion': constants.DEFAULT_API_VERSION, + 'location': '[resourceGroup().location]', + 'dependsOn': [ + '[concat(\'Microsoft.Compute/virtualMachines/\', variables(\'vmName\'))]' + ], + 'properties': { + 'publisher': 'Microsoft.Compute', + 'type': 'CustomScriptForLinux', + 'typeHandlerVersion': '1.5', + 'settings': { + 'fileUris': ["https://raw.githubusercontent.com/CatalystCode/pkgcloud/master/lib/pkgcloud/azure-v2/scripts/sudo.sh"], + 'commandToExecute': 'bash sudo.sh ' + options.username + }, } }]; } @@ -236,7 +261,7 @@ function destroyServer(server, options, callback) { var serverDetails; var nicsIds; var nicsDetails; - + var vnets; var publicIPs; @@ -255,7 +280,7 @@ function destroyServer(server, options, callback) { // Deleting the vm resourceClient = new resourceManagement.ResourceManagementClient(self.azure.credentials, self.config.subscriptionId); var client = new ComputeManagementClient(self.azure.credentials, self.config.subscriptionId); - client.virtualMachines.deleteMethod(self.config.resourceGroup, serverId, next); + client.virtualMachines.deleteMethod(self.config.resourceGroup, serverId, next); } ], function (err) { @@ -264,19 +289,19 @@ function destroyServer(server, options, callback) { } if (!options.destroyNics && - !options.destroyPublicIP && - !options.destroyVnet && - !options.destroyStorage) { + !options.destroyPublicIP && + !options.destroyVnet && + !options.destroyStorage) { return callback(); } async.waterfall([ function (next) { // Deleting the nics - nicsIds = serverDetails && - serverDetails.azure && - serverDetails.azure.networkProfile && - serverDetails.azure.networkProfile.networkInterfaces || []; + nicsIds = serverDetails && + serverDetails.azure && + serverDetails.azure.networkProfile && + serverDetails.azure.networkProfile.networkInterfaces || []; // Go over all nics, get their details and go on to delete them async.eachSeries(nicsIds, function (nic, cb) { @@ -329,7 +354,7 @@ function destroyServer(server, options, callback) { // Deleting public ips async.eachSeries(publicIPs, function (publicIP, cb) { resourceClient.resources.deleteById(publicIP, constants.MANAGEMENT_API_VERSION, cb); - }, next); + }, next); }, function (next) { @@ -340,7 +365,7 @@ function destroyServer(server, options, callback) { // Deleting vnets async.eachSeries(vnets, function (vnet, cb) { resourceClient.resources.deleteById(vnet, constants.MANAGEMENT_API_VERSION, cb); - }, next); + }, next); }, function (next) { // Deleting storage account @@ -348,12 +373,12 @@ function destroyServer(server, options, callback) { return next(); } - var storageUri = serverDetails && - serverDetails.azure && - serverDetails.azure.storageProfile && - serverDetails.azure.storageProfile.osDisk && - serverDetails.azure.storageProfile.osDisk.vhd && - serverDetails.azure.storageProfile.osDisk.vhd.uri || null; + var storageUri = serverDetails && + serverDetails.azure && + serverDetails.azure.storageProfile && + serverDetails.azure.storageProfile.osDisk && + serverDetails.azure.storageProfile.osDisk.vhd && + serverDetails.azure.storageProfile.osDisk.vhd.uri || null; if (!storageUri || !storageUri.startsWith('https://')) { return next(); @@ -364,17 +389,17 @@ function destroyServer(server, options, callback) { // Presuming the storage account is in the same resource group as the vm resourceClient.resources.deleteMethod( - self.config.resourceGroup, - 'Microsoft.Storage', - 'storageAccounts', - storageName, + self.config.resourceGroup, + 'Microsoft.Storage', + 'storageAccounts', + storageName, '', '2016-01-01', next); } ], function (error) { callback(error, serverDetails); }); }); - + } /** @@ -394,9 +419,9 @@ function stopServer(server, callback) { var client = new ComputeManagementClient(self.azure.credentials, self.config.subscriptionId); client.virtualMachines.powerOff(self.config.resourceGroup, serverId, function (err) { - return err - ? callback(err) - : callback(null, serverId); + return err ? + callback(err) : + callback(null, serverId); }); }); } @@ -418,9 +443,9 @@ function rebootServer(server, callback) { var client = new ComputeManagementClient(self.azure.credentials, self.config.subscriptionId); client.virtualMachines.restart(self.config.resourceGroup, serverId, function (err) { - return err - ? callback(err) - : callback(null, serverId); + return err ? + callback(err) : + callback(null, serverId); }); }); } @@ -432,7 +457,9 @@ function rebootServer(server, callback) { */ function renameServer(server, callback) { return errs.handle( - errs.create({ message: 'Not supported by Azure.' }), + errs.create({ + message: 'Not supported by Azure.' + }), callback ); } @@ -447,4 +474,4 @@ module.exports = { stopServer: stopServer, rebootServer: rebootServer, renameServer: renameServer -}; +}; \ No newline at end of file diff --git a/lib/pkgcloud/azure-v2/scripts/ssh.ps1 b/lib/pkgcloud/azure-v2/scripts/ssh.ps1 new file mode 100644 index 000000000..1c9e702cb --- /dev/null +++ b/lib/pkgcloud/azure-v2/scripts/ssh.ps1 @@ -0,0 +1,8 @@ +$username = $args[0] +$password = $args[1] +$securePassword = ConvertTo-SecureString $password -AsPlainText -Force +$credential = New-Object System.Management.Automation.PSCredential $username, $securePassword +Enable-PSRemoting -Force +$scriptPath = ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/DarwinJS/ChocoPackages/master/openssh/InstallChoco_and_win32-openssh_with_server.ps1')) +Invoke-Command -ScriptBlock ([scriptblock]::Create($scriptPath)) -Credential $credential -ComputerName localhost +Disable-PSRemoting -Force \ No newline at end of file diff --git a/lib/pkgcloud/azure-v2/scripts/sudo.sh b/lib/pkgcloud/azure-v2/scripts/sudo.sh new file mode 100644 index 000000000..430aee644 --- /dev/null +++ b/lib/pkgcloud/azure-v2/scripts/sudo.sh @@ -0,0 +1,4 @@ +#!/bin/bash +# Make sure you use a username that is lowercase. +USERNAME=$1 +echo "$USERNAME ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers \ No newline at end of file