From a87d9dfdcab36952703ede253c3f9386835877a3 Mon Sep 17 00:00:00 2001 From: Sumiran Aggarwal Date: Mon, 7 Oct 2019 15:10:53 +0530 Subject: [PATCH 1/2] adding login action --- action.yml | 13 ++++ lib/main.js | 69 +++++++++++++++++++ package-lock.json | 169 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 22 ++++++ src/main.ts | 51 ++++++++++++++ tsconfig.json | 60 ++++++++++++++++ 6 files changed, 384 insertions(+) create mode 100644 action.yml create mode 100644 lib/main.js create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 src/main.ts create mode 100644 tsconfig.json diff --git a/action.yml b/action.yml new file mode 100644 index 000000000..9613be7e1 --- /dev/null +++ b/action.yml @@ -0,0 +1,13 @@ +# Login to Azure subscription +name: 'Login Azure' +description: 'Login Azure wraps the az login, allowing for Azure actions to log into Azure' +inputs: + creds: + description: 'Paste output of `az ad sp create-for-rbac` as value of secret variable: AZURE_CREDENTIALS' + required: true +branding: + icon: 'login.svg' + color: 'blue' +runs: + using: 'node12' + main: 'lib/main.js' \ No newline at end of file diff --git a/lib/main.js b/lib/main.js new file mode 100644 index 000000000..0d845bb98 --- /dev/null +++ b/lib/main.js @@ -0,0 +1,69 @@ +"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 core = __importStar(require("@actions/core")); +const exec = __importStar(require("@actions/exec")); +const io = __importStar(require("@actions/io")); +const actions_secret_parser_1 = require("actions-secret-parser"); +var azPath; +function main() { + return __awaiter(this, void 0, void 0, function* () { + try { + azPath = yield io.which("az", true); + yield executeAzCliCommand("--version"); + let creds = core.getInput('creds', { required: true }); + let secrets = new actions_secret_parser_1.SecretParser(creds, actions_secret_parser_1.FormatType.JSON); + let servicePrincipalId = secrets.getSecret("$.clientId", true); + let servicePrincipalKey = secrets.getSecret("$.clientSecret", true); + let tenantId = secrets.getSecret("$.tenantId", true); + let subscriptionId = secrets.getSecret("$.subscriptionId", true); + 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}"`); + console.log("Login successful."); + } + catch (error) { + console.log("Login failed. Please check the credentials."); + core.setFailed(error); + } + }); +} +function executeAzCliCommand(command) { + return __awaiter(this, void 0, void 0, function* () { + let stdout = ''; + let stderr = ''; + let code = yield exec.exec(`"${azPath}" ${command}`, [], { + silent: true, + listeners: { + stdout: (data) => { + stdout += data.toString(); + }, + stderr: (data) => { + stderr += data.toString(); + } + } + }); + if (code != 0) { + throw new Error(stderr); + } + return stdout; + }); +} +main(); diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..17feb249a --- /dev/null +++ b/package-lock.json @@ -0,0 +1,169 @@ +{ + "name": "login", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@actions/core": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.1.3.tgz", + "integrity": "sha512-2BIib53Jh4Cfm+1XNuZYYGTeRo8yiWEAUMoliMh1qQGMaqTF4VUlhhcsBylTu4qWmUx45DrY0y0XskimAHSqhw==" + }, + "@actions/exec": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.0.1.tgz", + "integrity": "sha512-nvFkxwiicvpzNiCBF4wFBDfnBvi7xp/as7LE1hBxBxKG2L29+gkIPBiLKMVORL+Hg3JNf07AKRfl0V5djoypjQ==" + }, + "@actions/io": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.0.1.tgz", + "integrity": "sha512-rhq+tfZukbtaus7xyUtwKfuiCRXd1hWSfmJNEpFgBQJ4woqPEpsBw04awicjwz9tyG2/MVhAEMfVn664Cri5zA==" + }, + "@types/node": { + "version": "12.7.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.11.tgz", + "integrity": "sha512-Otxmr2rrZLKRYIybtdG/sgeO+tHY20GxeDjcGmUnmmlCWyEnv2a2x1ZXBo3BTec4OiTXMQCiazB8NMBf0iRlFw==", + "dev": true + }, + "actions-secret-parser": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/actions-secret-parser/-/actions-secret-parser-1.0.1.tgz", + "integrity": "sha512-4xenKyEwVp23mKVPUdP/NfipbrmFNbjf5+1FBAbGNdmMO+Awjoh4TnxrPdTy5yN7N6JUOBEtUgVVcOvzxfJu/Q==", + "requires": { + "@actions/core": "^1.1.3", + "jsonpath": "^1.0.2", + "xmldom": "^0.1.27", + "xpath": "0.0.27" + } + }, + "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=" + }, + "escodegen": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.12.0.tgz", + "integrity": "sha512-TuA+EhsanGcme5T3R0L80u4t8CpbXQjegRmf7+FPTJrtCTErXFeelblRgHQa1FofEzqYYJmJ/OqjTwREp9qgmg==", + "requires": { + "esprima": "^3.1.3", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + }, + "dependencies": { + "esprima": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", + "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=" + } + } + }, + "esprima": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.2.2.tgz", + "integrity": "sha1-dqD9Zvz+FU/SkmZ9wmQBl1CxZXs=" + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" + }, + "jsonpath": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/jsonpath/-/jsonpath-1.0.2.tgz", + "integrity": "sha512-rmzlgFZiQPc6q4HDyK8s9Qb4oxBnI5sF61y/Co5PV0lc3q2bIuRsNdueVbhoSHdKM4fxeimphOAtfz47yjCfeA==", + "requires": { + "esprima": "1.2.2", + "static-eval": "2.0.2", + "underscore": "1.7.0" + } + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.4", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "wordwrap": "~1.0.0" + } + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" + }, + "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 + }, + "static-eval": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.0.2.tgz", + "integrity": "sha512-N/D219Hcr2bPjLxPiV+TQE++Tsmrady7TqAJugLy7Xk1EumfDWS/f5dtBbkRCGE7wKKXuYockQoj8Rm2/pVKyg==", + "requires": { + "escodegen": "^1.8.1" + } + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "typescript": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.3.tgz", + "integrity": "sha512-N7bceJL1CtRQ2RiG0AQME13ksR7DiuQh/QehubYcghzv20tnh+MQnQIuJddTmsbqYj+dztchykemz0zFzlvdQw==", + "dev": true + }, + "underscore": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", + "integrity": "sha1-a7rwh3UA02vjTsqlhODbn+8DUgk=" + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" + }, + "xmldom": { + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.27.tgz", + "integrity": "sha1-1QH5ezvbQDr4757MIFcxh6rawOk=" + }, + "xpath": { + "version": "0.0.27", + "resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.27.tgz", + "integrity": "sha512-fg03WRxtkCV6ohClePNAECYsmpKKTv5L8y/X3Dn1hQrec3POx2jHZ/0P2qQ6HvsrU1BmeqXcof3NGGueG6LxwQ==" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 000000000..5c7391eeb --- /dev/null +++ b/package.json @@ -0,0 +1,22 @@ +{ + "name": "login", + "version": "1.0.0", + "description": "Login Azure wraps the az login, allowing for Azure actions to log into Azure", + "main": "lib/main.js", + "scripts": { + "build": "tsc", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Sumiran Aggarwal", + "license": "MIT", + "devDependencies": { + "@types/node": "^12.7.11", + "typescript": "^3.6.3" + }, + "dependencies": { + "@actions/core": "^1.1.3", + "@actions/exec": "^1.0.1", + "@actions/io": "^1.0.1", + "actions-secret-parser": "^1.0.1" + } +} diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 000000000..526df7402 --- /dev/null +++ b/src/main.ts @@ -0,0 +1,51 @@ +import * as core from '@actions/core'; +import * as exec from '@actions/exec'; +import * as io from '@actions/io'; + +import { FormatType, SecretParser } from 'actions-secret-parser'; + +var azPath: string; + +async function main() { + try{ + azPath = await io.which("az", true); + await executeAzCliCommand("--version"); + + let creds = core.getInput('creds', { required: true }); + let secrets = new SecretParser(creds, FormatType.JSON); + let servicePrincipalId = secrets.getSecret("$.clientId", false); + let servicePrincipalKey = secrets.getSecret("$.clientSecret", true); + let tenantId = secrets.getSecret("$.tenantId", false); + let subscriptionId = secrets.getSecret("$.subscriptionId", false); + 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"); + } + + await executeAzCliCommand(`login --service-principal -u "${servicePrincipalId}" -p "${servicePrincipalKey}" --tenant "${tenantId}"`); + await executeAzCliCommand(`account set --subscription "${subscriptionId}"`); + console.log("Login successful."); + } catch (error) { + console.log("Login failed. Please check the credentials."); + core.setFailed(error); + } +} + +async function executeAzCliCommand(command: string) { + let stderr = ''; + + let code = await exec.exec(`"${azPath}" ${command}`, [], { + failOnStdErr: true, + listeners: { + stderr: (data: Buffer) => { + stderr += data.toString(); + } + } + }); + + if(code != 0) { + throw new Error(stderr); + } +} + + +main(); \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 000000000..7a865ad11 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,60 @@ +{ + "compilerOptions": { + /* Basic Options */ + "target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */ + "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ + // "lib": [], /* Specify library files to be included in the compilation. */ + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + // "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + "outDir": "./lib", /* Redirect output structure to the directory. */ + "rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": false, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + + /* Module Resolution Options */ + // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + } +} From 30ec3d2de07176d43357d76b58a8d3f4e45faed2 Mon Sep 17 00:00:00 2001 From: Sumiran Aggarwal Date: Wed, 9 Oct 2019 15:29:04 +0530 Subject: [PATCH 2/2] build js file --- lib/main.js | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/lib/main.js b/lib/main.js index 0d845bb98..56f6afb88 100644 --- a/lib/main.js +++ b/lib/main.js @@ -28,10 +28,10 @@ function main() { yield executeAzCliCommand("--version"); let creds = core.getInput('creds', { required: true }); let secrets = new actions_secret_parser_1.SecretParser(creds, actions_secret_parser_1.FormatType.JSON); - let servicePrincipalId = secrets.getSecret("$.clientId", true); + let servicePrincipalId = secrets.getSecret("$.clientId", false); let servicePrincipalKey = secrets.getSecret("$.clientSecret", true); - let tenantId = secrets.getSecret("$.tenantId", true); - let subscriptionId = secrets.getSecret("$.subscriptionId", true); + let tenantId = secrets.getSecret("$.tenantId", false); + let subscriptionId = secrets.getSecret("$.subscriptionId", false); 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"); } @@ -47,14 +47,10 @@ function main() { } function executeAzCliCommand(command) { return __awaiter(this, void 0, void 0, function* () { - let stdout = ''; let stderr = ''; let code = yield exec.exec(`"${azPath}" ${command}`, [], { - silent: true, + failOnStdErr: true, listeners: { - stdout: (data) => { - stdout += data.toString(); - }, stderr: (data) => { stderr += data.toString(); } @@ -63,7 +59,6 @@ function executeAzCliCommand(command) { if (code != 0) { throw new Error(stderr); } - return stdout; }); } main();