diff --git a/lib/PowerShell/Constants.js b/lib/PowerShell/Constants.js index e47bba72..98f75ccf 100644 --- a/lib/PowerShell/Constants.js +++ b/lib/PowerShell/Constants.js @@ -6,6 +6,9 @@ exports.default = 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"; +Constants.AzureCloud = "AzureCloud"; +Constants.Subscription = "Subscription"; +Constants.ServicePrincipal = "ServicePrincipal"; +Constants.Success = "Success"; +Constants.Error = "Error"; +Constants.AzVersion = "AzVersion"; diff --git a/lib/PowerShell/ServicePrincipalLogin.js b/lib/PowerShell/ServicePrincipalLogin.js index 9dadd9e3..59900bd4 100644 --- a/lib/PowerShell/ServicePrincipalLogin.js +++ b/lib/PowerShell/ServicePrincipalLogin.js @@ -34,21 +34,59 @@ class ServicePrincipalLogin { initialize() { return __awaiter(this, void 0, void 0, function* () { Utils_1.default.setPSModulePath(); - const azLatestVersion = yield Utils_1.default.getLatestModule(Constants_1.default.moduleName); + const script = new ScriptBuilder_1.default().getLatestModuleScript(Constants_1.default.moduleName); + const outputJson = yield this.getLatestModule(script); + const azLatestVersion = outputJson.Constants.AzPSVersion; + if (!(Constants_1.default.Success in outputJson) || !Utils_1.default.isValidVersion(azLatestVersion)) { + throw new Error(`Invalid AzPSVersion: ${azLatestVersion}`); + } core.debug(`Az Module version used: ${azLatestVersion}`); Utils_1.default.setPSModulePath(`${Constants_1.default.prefix}${azLatestVersion}`); }); } + getLatestModule(script) { + return __awaiter(this, void 0, void 0, function* () { + let output = ""; + const options = { + listeners: { + stdout: (data) => { + output += data.toString(); + } + } + }; + yield PowerShellToolRunner_1.default.init(); + yield PowerShellToolRunner_1.default.executePowerShellCommand(script, options); + return JSON.parse(output.trim()); + }); + } login() { return __awaiter(this, void 0, void 0, function* () { + let output = ""; + const options = { + listeners: { + stdout: (data) => { + output += data.toString(); + } + } + }; + const args = { + servicePrincipalId: this.servicePrincipalId, + servicePrincipalKey: this.servicePrincipalKey, + subscriptionId: this.subscriptionId, + environment: ServicePrincipalLogin.environment, + scopeLevel: ServicePrincipalLogin.scopeLevel + }; + const script = new ScriptBuilder_1.default().getAzPSLoginScript(ServicePrincipalLogin.scheme, this.tenantId, args); yield 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); + yield PowerShellToolRunner_1.default.executePowerShellCommand(script, options); + const outputJson = JSON.parse(output.trim()); + if (!(Constants_1.default.Success in outputJson)) { + throw new Error(`Azure PowerShell login failed with error: ${outputJson.Constants.Error}`); + } }); } } exports.ServicePrincipalLogin = ServicePrincipalLogin; -ServicePrincipalLogin.environment = Constants_1.default.environment; -ServicePrincipalLogin.scopeLevel = Constants_1.default.scopeLevel; -ServicePrincipalLogin.scheme = Constants_1.default.scheme; +ServicePrincipalLogin.environment = Constants_1.default.AzureCloud; +ServicePrincipalLogin.scopeLevel = Constants_1.default.Subscription; +ServicePrincipalLogin.scheme = Constants_1.default.ServicePrincipal; diff --git a/lib/PowerShell/Utilities/ScriptBuilder.js b/lib/PowerShell/Utilities/ScriptBuilder.js index 68fd9c1e..0308a0bd 100644 --- a/lib/PowerShell/Utilities/ScriptBuilder.js +++ b/lib/PowerShell/Utilities/ScriptBuilder.js @@ -1,20 +1,60 @@ "use strict"; +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 Constants_1 = __importDefault(require("../Constants")); class ScriptBuilder { constructor() { this.script = ""; } - 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};`; + getAzPSLoginScript(scheme, tenantId, args) { + let command = `Clear-AzContext -Scope Process; + Clear-AzContext -Scope CurrentUser -Force -ErrorAction SilentlyContinue;`; + if (scheme === Constants_1.default.ServicePrincipal) { + command += `Connect-AzAccount -ServicePrincipal -Tenant ${tenantId} -Credential \ + (New-Object System.Management.Automation.PSCredential('${args.servicePrincipalId}',(ConvertTo-SecureString ${args.servicePrincipalKey} -AsPlainText -Force))) \ + -Environment ${args.environment};`; + if (args.scopeLevel === Constants_1.default.Subscription) { + command += `Set-AzContext -SubscriptionId ${args.subscriptionId} -TenantId ${tenantId};`; } } - this.script += `Get-AzContext`; + command += `Get-AzContext`; + this.script += `try { + $$ErrorActionPreference = "Stop"; + $$output = @{}; + ${command} + $$output['${Constants_1.default.Success}'] = "true"; + } + catch { + $$output['${Constants_1.default.Error}'] = $$_.exception.Message; + } + return ConvertTo-Json $$a`; + core.debug(`Azure PowerShell Login Script: ${this.script}`); + return this.script; + } + getLatestModuleScript(moduleName) { + const command = `Get-Module -Name ${moduleName} -ListAvailable | Sort-Object Version -Descending | Select-Object -First 1`; + this.script += `try { + $$ErrorActionPreference = "Stop"; + $$output = @{}; + $$data = ${command}; + $$output['${Constants_1.default.AzVersion}'] = $$data.Version.ToString(); + $$output['${Constants_1.default.Success}'] = "true"; + } + catch { + $$output['${Constants_1.default.Error}'] = $$_.exception.Message; + } + return ConvertTo-Json $$a`; + core.debug(`GetLatestModuleScript: ${this.script}`); return this.script; } } diff --git a/lib/PowerShell/Utilities/Utils.js b/lib/PowerShell/Utilities/Utils.js index e32d79ae..e0d6f284 100644 --- a/lib/PowerShell/Utilities/Utils.js +++ b/lib/PowerShell/Utilities/Utils.js @@ -1,13 +1,4 @@ "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 = {}; @@ -21,33 +12,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { Object.defineProperty(exports, "__esModule", { value: true }); const os = __importStar(require("os")); const Constants_1 = __importDefault(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(); - } - } - }; - yield 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.default.versionPattern); - } static setPSModulePath(azPSVersion = "") { let modulePath = ""; const runner = process.env.RUNNER_OS || os.type(); @@ -64,9 +29,12 @@ class Utils { // TODO: add modulepath break; default: - throw new Error("Unknown os"); + throw new Error(`Unknown os: ${runner.toLowerCase()}`); } process.env.PSModulePath = `${modulePath}${process.env.PSModulePath}`; } + static isValidVersion(version) { + return !!version.match(Constants_1.default.versionPattern); + } } exports.default = Utils; diff --git a/lib/main.js b/lib/main.js index 4948a2a9..ca968e8b 100644 --- a/lib/main.js +++ b/lib/main.js @@ -44,13 +44,15 @@ function main() { 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."); } + // Attempting Az cli login yield executeAzCliCommand(`login --service-principal -u "${servicePrincipalId}" -p "${servicePrincipalKey}" --tenant "${tenantId}"`); yield executeAzCliCommand(`account set --subscription "${subscriptionId}"`); if (enablePSSession) { + // Attempting Az PS login console.log(`Running Azure PS Login`); const spnlogin = new ServicePrincipalLogin_1.ServicePrincipalLogin(servicePrincipalId, servicePrincipalKey, tenantId, subscriptionId); - spnlogin.initialize(); - spnlogin.login(); + yield spnlogin.initialize(); + yield spnlogin.login(); } console.log("Login successful."); } diff --git a/package-lock.json b/package-lock.json index c3e3c9b5..e8b32c12 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,24 @@ "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.0.1.tgz", "integrity": "sha512-rhq+tfZukbtaus7xyUtwKfuiCRXd1hWSfmJNEpFgBQJ4woqPEpsBw04awicjwz9tyG2/MVhAEMfVn664Cri5zA==" }, + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "requires": { + "@babel/highlight": "^7.8.3" + } + }, + "@babel/highlight": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", + "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + } + }, "@types/node": { "version": "12.7.11", "resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.11.tgz", @@ -36,11 +54,89 @@ "xpath": "0.0.27" } }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=" + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, "escodegen": { "version": "1.12.0", "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.12.0.tgz", @@ -80,6 +176,64 @@ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "dependencies": { + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + } + } + }, "jsonpath": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/jsonpath/-/jsonpath-1.0.2.tgz", @@ -99,6 +253,35 @@ "type-check": "~0.3.2" } }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "mkdirp": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.3.tgz", + "integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==", + "requires": { + "minimist": "^1.2.5" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, "optionator": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", @@ -112,17 +295,45 @@ "word-wrap": "~1.2.3" } }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + }, "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" }, + "resolve": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", + "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", + "requires": { + "path-parse": "^1.0.6" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "optional": true }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + }, "static-eval": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.0.2.tgz", @@ -131,6 +342,47 @@ "escodegen": "^1.8.1" } }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + }, + "tslib": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz", + "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==" + }, + "tslint": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.0.tgz", + "integrity": "sha512-fXjYd/61vU6da04E505OZQGb2VCN2Mq3doeWcOIryuG+eqdmFUXTYVwdhnbEu2k46LNLgUYt9bI5icQze/j0bQ==", + "requires": { + "@babel/code-frame": "^7.0.0", + "builtin-modules": "^1.1.1", + "chalk": "^2.3.0", + "commander": "^2.12.1", + "diff": "^4.0.1", + "glob": "^7.1.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "resolve": "^1.3.2", + "semver": "^5.3.0", + "tslib": "^1.10.0", + "tsutils": "^2.29.0" + } + }, + "tsutils": { + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", + "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", + "requires": { + "tslib": "^1.8.1" + } + }, "type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", @@ -155,6 +407,11 @@ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==" }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, "xmldom": { "version": "0.1.27", "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.27.tgz", diff --git a/package.json b/package.json index b0b60823..26e25c46 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "@actions/core": "^1.1.3", "@actions/exec": "^1.0.1", "@actions/io": "^1.0.1", - "actions-secret-parser": "^1.0.2" + "actions-secret-parser": "^1.0.2", + "tslint": "^6.1.0" } } diff --git a/src/PowerShell/Constants.ts b/src/PowerShell/Constants.ts index 2e85a96e..9613db1d 100644 --- a/src/PowerShell/Constants.ts +++ b/src/PowerShell/Constants.ts @@ -3,7 +3,11 @@ export default class Constants { static readonly moduleName: string = "Az.Accounts"; static readonly versionPattern = /[0-9]\.[0-9]\.[0-9]/; - static readonly environment: string = "AzureCloud"; - static readonly scopeLevel: string = "Subscription"; - static readonly scheme: string = "ServicePrincipal"; + static readonly AzureCloud: string = "AzureCloud"; + static readonly Subscription: string = "Subscription"; + static readonly ServicePrincipal: string = "ServicePrincipal"; + + static readonly Success: string = "Success"; + static readonly Error: string = "Error"; + static readonly AzVersion: string = "AzVersion"; } \ No newline at end of file diff --git a/src/PowerShell/ServicePrincipalLogin.ts b/src/PowerShell/ServicePrincipalLogin.ts index 7760e97c..f4ac6d0c 100644 --- a/src/PowerShell/ServicePrincipalLogin.ts +++ b/src/PowerShell/ServicePrincipalLogin.ts @@ -6,9 +6,9 @@ import ScriptBuilder from './Utilities/ScriptBuilder'; import Constants from './Constants'; export class ServicePrincipalLogin implements IAzurePowerShellSession { - static readonly environment: string = Constants.environment; - static readonly scopeLevel: string = Constants.scopeLevel; - static readonly scheme: string = Constants.scheme; + static readonly environment: string = Constants.AzureCloud; + static readonly scopeLevel: string = Constants.Subscription; + static readonly scheme: string = Constants.ServicePrincipal; servicePrincipalId: string; servicePrincipalKey: string; tenantId: string; @@ -23,17 +23,53 @@ export class ServicePrincipalLogin implements IAzurePowerShellSession { async initialize() { Utils.setPSModulePath(); - const azLatestVersion: string = await Utils.getLatestModule(Constants.moduleName); + const script: string = new ScriptBuilder().getLatestModuleScript(Constants.moduleName); + const outputJson = await this.getLatestModule(script); + const azLatestVersion: string = outputJson.Constants.AzPSVersion; + if (!(Constants.Success in outputJson) || !Utils.isValidVersion(azLatestVersion)) { + throw new Error(`Invalid AzPSVersion: ${azLatestVersion}`); + } core.debug(`Az Module version used: ${azLatestVersion}`); Utils.setPSModulePath(`${Constants.prefix}${azLatestVersion}`); } - async login() { + private async getLatestModule(script: string): Promise { + let output: string = ""; + const options: any = { + listeners: { + stdout: (data: Buffer) => { + output += data.toString(); + } + } + }; await PowerShellToolRunner.init(); - const scriptBuilder: ScriptBuilder = new ScriptBuilder(); - const script: string = scriptBuilder.getScript(ServicePrincipalLogin.scheme, this.tenantId, this.servicePrincipalId, this.servicePrincipalKey, - this.subscriptionId, ServicePrincipalLogin.environment, ServicePrincipalLogin.scopeLevel); - PowerShellToolRunner.executePowerShellCommand(script); + await PowerShellToolRunner.executePowerShellCommand(script, options); + return JSON.parse(output.trim()); + } + + async login() { + let output: string = ""; + const options: any = { + listeners: { + stdout: (data: Buffer) => { + output += data.toString(); + } + } + }; + const args: any = { + servicePrincipalId: this.servicePrincipalId, + servicePrincipalKey: this.servicePrincipalKey, + subscriptionId: this.subscriptionId, + environment: ServicePrincipalLogin.environment, + scopeLevel: ServicePrincipalLogin.scopeLevel + } + const script: string = new ScriptBuilder().getAzPSLoginScript(ServicePrincipalLogin.scheme, this.tenantId, args); + await PowerShellToolRunner.init(); + await PowerShellToolRunner.executePowerShellCommand(script, options); + const outputJson: any = JSON.parse(output.trim()); + if (!(Constants.Success in outputJson)) { + throw new Error(`Azure PowerShell login failed with error: ${outputJson.Constants.Error}`); + } } } \ No newline at end of file diff --git a/src/PowerShell/Utilities/ScriptBuilder.ts b/src/PowerShell/Utilities/ScriptBuilder.ts index 8895d227..43b33b85 100644 --- a/src/PowerShell/Utilities/ScriptBuilder.ts +++ b/src/PowerShell/Utilities/ScriptBuilder.ts @@ -1,16 +1,50 @@ +import * as core from '@actions/core'; + +import Constants from "../Constants"; + export default class ScriptBuilder { script: string = ""; - getScript(scheme: string, tenantId: string, servicePrincipalId: string, servicePrincipalKey: string, subscriptionId: string, environment: string, scopeLevel: string): string { - 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};`; + + getAzPSLoginScript(scheme: string, tenantId: string, args: any): string { + let command = `Clear-AzContext -Scope Process; + Clear-AzContext -Scope CurrentUser -Force -ErrorAction SilentlyContinue;`; + if (scheme === Constants.ServicePrincipal) { + command += `Connect-AzAccount -ServicePrincipal -Tenant ${tenantId} -Credential \ + (New-Object System.Management.Automation.PSCredential('${args.servicePrincipalId}',(ConvertTo-SecureString ${args.servicePrincipalKey} -AsPlainText -Force))) \ + -Environment ${args.environment};`; + if (args.scopeLevel === Constants.Subscription) { + command += `Set-AzContext -SubscriptionId ${args.subscriptionId} -TenantId ${tenantId};`; } } - this.script += `Get-AzContext`; + command += `Get-AzContext`; + this.script += `try { + $$ErrorActionPreference = "Stop"; + $$output = @{}; + ${command} + $$output['${Constants.Success}'] = "true"; + } + catch { + $$output['${Constants.Error}'] = $$_.exception.Message; + } + return ConvertTo-Json $$a`; + core.debug(`Azure PowerShell Login Script: ${this.script}`); return this.script; } -} \ No newline at end of file + + getLatestModuleScript(moduleName: string): string { + const command: string = `Get-Module -Name ${moduleName} -ListAvailable | Sort-Object Version -Descending | Select-Object -First 1`; + this.script += `try { + $$ErrorActionPreference = "Stop"; + $$output = @{}; + $$data = ${command}; + $$output['${Constants.AzVersion}'] = $$data.Version.ToString(); + $$output['${Constants.Success}'] = "true"; + } + catch { + $$output['${Constants.Error}'] = $$_.exception.Message; + } + return ConvertTo-Json $$a`; + core.debug(`GetLatestModuleScript: ${this.script}`); + return this.script; + } +} diff --git a/src/PowerShell/Utilities/Utils.ts b/src/PowerShell/Utilities/Utils.ts index 9a25a72b..1428c6ef 100644 --- a/src/PowerShell/Utilities/Utils.ts +++ b/src/PowerShell/Utilities/Utils.ts @@ -1,36 +1,9 @@ import * as os from 'os'; -import * as exec from '@actions/exec'; -import * as io from '@actions/io'; import Constants from '../Constants'; import PowerShellToolRunner from './PowerShellToolRunner'; export default class Utils { - static async getLatestModule(moduleName: string): Promise { - let output: string = ""; - let error: string = ""; - const options: any = { - listeners: { - stdout: (data: Buffer) => { - output += data.toString(); - }, - stderr: (data: Buffer) => { - error += data.toString(); - } - } - }; - await PowerShellToolRunner.init(); - await PowerShellToolRunner.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(); - } - - private static isValidVersion(version: string): boolean { - return !!version.match(Constants.versionPattern); - } - static setPSModulePath(azPSVersion: string = "") { let modulePath: string = ""; const runner: string = process.env.RUNNER_OS || os.type(); @@ -47,9 +20,13 @@ export default class Utils { // TODO: add modulepath break; default: - throw new Error("Unknown os"); + throw new Error(`Unknown os: ${runner.toLowerCase()}`); } process.env.PSModulePath = `${modulePath}${process.env.PSModulePath}`; } + + static isValidVersion(version: string): boolean { + return !!version.match(Constants.versionPattern); + } } diff --git a/src/main.ts b/src/main.ts index 86b0ad00..a9e7f461 100644 --- a/src/main.ts +++ b/src/main.ts @@ -30,13 +30,15 @@ async function main() { 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."); } + // Attempting Az cli login await executeAzCliCommand(`login --service-principal -u "${servicePrincipalId}" -p "${servicePrincipalKey}" --tenant "${tenantId}"`); await executeAzCliCommand(`account set --subscription "${subscriptionId}"`); if (enablePSSession) { + // Attempting Az PS login console.log(`Running Azure PS Login`); const spnlogin: ServicePrincipalLogin = new ServicePrincipalLogin(servicePrincipalId, servicePrincipalKey, tenantId, subscriptionId); - spnlogin.initialize(); - spnlogin.login(); + await spnlogin.initialize(); + await spnlogin.login(); } console.log("Login successful."); } catch (error) {