From 7fc98cfa3dfb7e5546b31e7c725cea3f0bd9ba70 Mon Sep 17 00:00:00 2001 From: Akshaya M Date: Mon, 16 Mar 2020 17:01:10 +0530 Subject: [PATCH] Code refactor --- lib/PowerShell/Constants.js | 11 +++ lib/PowerShell/IAzurePowerShellSession.js | 0 lib/PowerShell/ServicePrincipalLogin.js | 54 ++++++++++++++ .../Utilities/PowerShellToolRunner.js | 35 +++++++++ lib/PowerShell/Utilities/ScriptBuilder.js | 18 +++++ lib/PowerShell/Utilities/Utils.js | 72 +++++++++++++++++++ lib/main.js | 10 ++- src/PowerShell/ServicePrincipalLogin.ts | 6 +- .../Utilities/PowerShellToolRunner.ts | 6 +- src/PowerShell/Utilities/Utils.ts | 2 +- src/main.ts | 2 +- 11 files changed, 204 insertions(+), 12 deletions(-) create mode 100644 lib/PowerShell/Constants.js create mode 100644 lib/PowerShell/IAzurePowerShellSession.js create mode 100644 lib/PowerShell/ServicePrincipalLogin.js create mode 100644 lib/PowerShell/Utilities/PowerShellToolRunner.js create mode 100644 lib/PowerShell/Utilities/ScriptBuilder.js create mode 100644 lib/PowerShell/Utilities/Utils.js diff --git a/lib/PowerShell/Constants.js b/lib/PowerShell/Constants.js new file mode 100644 index 00000000..49ab99a7 --- /dev/null +++ b/lib/PowerShell/Constants.js @@ -0,0 +1,11 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +class Constants { +} +exports.Constants = Constants; +Constants.prefix = "az_"; +Constants.moduleName = "Az.Accounts"; +Constants.versionPattern = /[0-9]\.[0-9]\.[0-9]/; +Constants.environment = "AzureCloud"; +Constants.scopeLevel = "Subscription"; +Constants.scheme = "ServicePrincipal"; diff --git a/lib/PowerShell/IAzurePowerShellSession.js b/lib/PowerShell/IAzurePowerShellSession.js new file mode 100644 index 00000000..e69de29b diff --git a/lib/PowerShell/ServicePrincipalLogin.js b/lib/PowerShell/ServicePrincipalLogin.js new file mode 100644 index 00000000..ef4e60e6 --- /dev/null +++ b/lib/PowerShell/ServicePrincipalLogin.js @@ -0,0 +1,54 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; + result["default"] = mod; + return result; +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const core = __importStar(require("@actions/core")); +const Utils_1 = __importDefault(require("./Utilities/Utils")); +const PowerShellToolRunner_1 = __importDefault(require("./Utilities/PowerShellToolRunner")); +const ScriptBuilder_1 = __importDefault(require("./Utilities/ScriptBuilder")); +const Constants_1 = require("./Constants"); +class ServicePrincipalLogin { + constructor(servicePrincipalId, servicePrincipalKey, tenantId, subscriptionId) { + this.servicePrincipalId = servicePrincipalId; + this.servicePrincipalKey = servicePrincipalKey; + this.tenantId = tenantId; + this.subscriptionId = subscriptionId; + } + initialize() { + return __awaiter(this, void 0, void 0, function* () { + Utils_1.default.setPSModulePath(); + const azLatestVersion = yield Utils_1.default.getLatestModule(Constants_1.Constants.moduleName); + core.debug(`Az Module version used: ${azLatestVersion}`); + Utils_1.default.setPSModulePath(`${Constants_1.Constants.prefix}${azLatestVersion}`); + }); + } + login() { + return __awaiter(this, void 0, void 0, function* () { + PowerShellToolRunner_1.default.init(); + const scriptBuilder = new ScriptBuilder_1.default(); + const script = scriptBuilder.getScript(ServicePrincipalLogin.scheme, this.tenantId, this.servicePrincipalId, this.servicePrincipalKey, this.subscriptionId, ServicePrincipalLogin.environment, ServicePrincipalLogin.scopeLevel); + PowerShellToolRunner_1.default.executePowerShellCommand(script); + }); + } +} +exports.ServicePrincipalLogin = ServicePrincipalLogin; +ServicePrincipalLogin.environment = Constants_1.Constants.environment; +ServicePrincipalLogin.scopeLevel = Constants_1.Constants.scopeLevel; +ServicePrincipalLogin.scheme = Constants_1.Constants.scheme; diff --git a/lib/PowerShell/Utilities/PowerShellToolRunner.js b/lib/PowerShell/Utilities/PowerShellToolRunner.js new file mode 100644 index 00000000..7205551b --- /dev/null +++ b/lib/PowerShell/Utilities/PowerShellToolRunner.js @@ -0,0 +1,35 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; + result["default"] = mod; + return result; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const io = __importStar(require("@actions/io")); +const exec = __importStar(require("@actions/exec")); +class PowerShellToolRunner { + static init() { + return __awaiter(this, void 0, void 0, function* () { + if (!PowerShellToolRunner.psPath) { + PowerShellToolRunner.psPath = yield io.which("pwsh", true); + } + }); + } + static executePowerShellCommand(command, options = {}) { + return __awaiter(this, void 0, void 0, function* () { + yield exec.exec(`"${PowerShellToolRunner.psPath}" -Command "${command}"`, [], options); + }); + } +} +exports.default = PowerShellToolRunner; diff --git a/lib/PowerShell/Utilities/ScriptBuilder.js b/lib/PowerShell/Utilities/ScriptBuilder.js new file mode 100644 index 00000000..68bc01f1 --- /dev/null +++ b/lib/PowerShell/Utilities/ScriptBuilder.js @@ -0,0 +1,18 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +class ScriptBuilder { + getScript(scheme, tenantId, servicePrincipalId, servicePrincipalKey, subscriptionId, environment, scopeLevel) { + this.script += `Clear-AzContext -Scope Process; Clear-AzContext -Scope CurrentUser -Force -ErrorAction SilentlyContinue;`; + if (scheme === "ServicePrincipal") { + this.script += `Connect-AzAccount -ServicePrincipal -Tenant ${tenantId} -Credential \ + (New-Object System.Management.Automation.PSCredential('${servicePrincipalId}',(ConvertTo-SecureString ${servicePrincipalKey} -AsPlainText -Force))) \ + -Environment ${environment};`; + if (scopeLevel === "Subscription") { + this.script += `Set-AzContext -SubscriptionId ${subscriptionId} -TenantId ${tenantId};`; + } + } + this.script += `Get-AzContext`; + return this.script; + } +} +exports.default = ScriptBuilder; diff --git a/lib/PowerShell/Utilities/Utils.js b/lib/PowerShell/Utilities/Utils.js new file mode 100644 index 00000000..d23b998e --- /dev/null +++ b/lib/PowerShell/Utilities/Utils.js @@ -0,0 +1,72 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; + result["default"] = mod; + return result; +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const os = __importStar(require("os")); +const Constants_1 = require("../Constants"); +const PowerShellToolRunner_1 = __importDefault(require("./PowerShellToolRunner")); +class Utils { + static getLatestModule(moduleName) { + return __awaiter(this, void 0, void 0, function* () { + let output = ""; + let error = ""; + const options = { + listeners: { + stdout: (data) => { + output += data.toString(); + }, + stderr: (data) => { + error += data.toString(); + } + } + }; + PowerShellToolRunner_1.default.init(); + yield PowerShellToolRunner_1.default.executePowerShellCommand(`(Get-Module -Name ${moduleName} -ListAvailable | Sort-Object Version -Descending | Select-Object -First 1).Version.ToString()`, options); + if (!Utils.isValidVersion(output.trim())) { + return ""; + } + return output.trim(); + }); + } + static isValidVersion(version) { + return !!version.match(Constants_1.Constants.versionPattern); + } + static setPSModulePath(azPSVersion = "") { + let modulePath = ""; + const RUNNER = process.env.RUNNER_OS || os.type(); + switch (RUNNER) { + case "Linux": + modulePath = `/usr/share/${azPSVersion}:`; + break; + case "Windows": + case "Windows_NT": + modulePath = `C:\\Modules\\${azPSVersion};`; + break; + case "macOS": + case "Darwin": + // TODO: add modulepath + break; + default: + throw new Error("Unknown os"); + } + process.env.PSModulePath = `${modulePath}${process.env.PSModulePath}`; + } +} +exports.default = Utils; diff --git a/lib/main.js b/lib/main.js index 226622d4..ba89d83d 100644 --- a/lib/main.js +++ b/lib/main.js @@ -21,7 +21,7 @@ const crypto = __importStar(require("crypto")); const exec = __importStar(require("@actions/exec")); const io = __importStar(require("@actions/io")); const actions_secret_parser_1 = require("actions-secret-parser"); -const loginAzurePowerShell_1 = require("./loginAzurePowerShell"); +const ServicePrincipalLogin_1 = require("./PowerShell/ServicePrincipalLogin"); var azPath; var prefix = !!process.env.AZURE_HTTP_USER_AGENT ? `${process.env.AZURE_HTTP_USER_AGENT}` : ""; function main() { @@ -40,12 +40,18 @@ function main() { let servicePrincipalKey = secrets.getSecret("$.clientSecret", true); let tenantId = secrets.getSecret("$.tenantId", false); let subscriptionId = secrets.getSecret("$.subscriptionId", false); + const enablePSSession = !!core.getInput('enable-PSSession'); if (!servicePrincipalId || !servicePrincipalKey || !tenantId || !subscriptionId) { throw new Error("Not all values are present in the creds object. Ensure clientId, clientSecret, tenantId and subscriptionId are supplied."); } yield executeAzCliCommand(`login --service-principal -u "${servicePrincipalId}" -p "${servicePrincipalKey}" --tenant "${tenantId}"`); yield executeAzCliCommand(`account set --subscription "${subscriptionId}"`); - yield loginAzurePowerShell_1.initializeAz(servicePrincipalId, servicePrincipalKey, tenantId, subscriptionId); + if (enablePSSession) { + console.log(`Running Azure PS Login`); + const spnlogin = new ServicePrincipalLogin_1.ServicePrincipalLogin(servicePrincipalId, servicePrincipalKey, tenantId, subscriptionId); + spnlogin.initialize(); + spnlogin.login(); + } console.log("Login successful."); } catch (error) { diff --git a/src/PowerShell/ServicePrincipalLogin.ts b/src/PowerShell/ServicePrincipalLogin.ts index b7f00a90..5b291c0a 100644 --- a/src/PowerShell/ServicePrincipalLogin.ts +++ b/src/PowerShell/ServicePrincipalLogin.ts @@ -1,8 +1,8 @@ import * as core from '@actions/core'; -import Utils from './Utils'; -import PowerShellToolRunner from './PowerShellToolRunner'; -import ScriptBuilder from './ScriptBuilder'; +import Utils from './Utilities/Utils'; +import PowerShellToolRunner from './Utilities/PowerShellToolRunner'; +import ScriptBuilder from './Utilities/ScriptBuilder'; import { Constants } from './Constants'; export class ServicePrincipalLogin implements IAzurePowerShellSession { diff --git a/src/PowerShell/Utilities/PowerShellToolRunner.ts b/src/PowerShell/Utilities/PowerShellToolRunner.ts index 57eb5783..86014d79 100644 --- a/src/PowerShell/Utilities/PowerShellToolRunner.ts +++ b/src/PowerShell/Utilities/PowerShellToolRunner.ts @@ -11,10 +11,6 @@ export default class PowerShellToolRunner { } static async executePowerShellCommand(command: string, options: any = {}) { - try { - await exec.exec(`"${PowerShellToolRunner.psPath}" -Command "${command}"`, [], options); - } catch(error) { - throw new Error(error); - } + await exec.exec(`"${PowerShellToolRunner.psPath}" -Command "${command}"`, [], options); } } \ No newline at end of file diff --git a/src/PowerShell/Utilities/Utils.ts b/src/PowerShell/Utilities/Utils.ts index f7ebcadc..a3e62590 100644 --- a/src/PowerShell/Utilities/Utils.ts +++ b/src/PowerShell/Utilities/Utils.ts @@ -2,7 +2,7 @@ import * as os from 'os'; import * as exec from '@actions/exec'; import * as io from '@actions/io'; -import { Constants } from './Constants'; +import { Constants } from '../Constants'; import PowerShellToolRunner from './PowerShellToolRunner'; export default class Utils { diff --git a/src/main.ts b/src/main.ts index 949f7879..f81f26d7 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,7 +4,7 @@ import * as exec from '@actions/exec'; import * as io from '@actions/io'; import { FormatType, SecretParser } from 'actions-secret-parser'; -import { ServicePrincipalLogin } from './ServicePrincipalLogin'; +import { ServicePrincipalLogin } from './PowerShell/ServicePrincipalLogin'; var azPath: string; var prefix = !!process.env.AZURE_HTTP_USER_AGENT ? `${process.env.AZURE_HTTP_USER_AGENT}` : "";