Code refactor

This commit is contained in:
Akshaya M
2020-03-16 15:15:59 +05:30
parent 1c43646e3a
commit 3e94d97948
8 changed files with 151 additions and 78 deletions

View File

@@ -0,0 +1,9 @@
export class Constants {
static readonly prefix: string = "az_";
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";
}

View File

@@ -0,0 +1,4 @@
interface IAzurePowerShellSession {
initialize();
login();
}

View File

@@ -0,0 +1,39 @@
import * as core from '@actions/core';
import Utils from './Utils';
import PowerShellToolRunner from './PowerShellToolRunner';
import ScriptBuilder from './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;
servicePrincipalId: string;
servicePrincipalKey: string;
tenantId: string;
subscriptionId: string;
constructor(servicePrincipalId: string, servicePrincipalKey: string, tenantId: string, subscriptionId: string) {
this.servicePrincipalId = servicePrincipalId;
this.servicePrincipalKey = servicePrincipalKey;
this.tenantId = tenantId;
this.subscriptionId = subscriptionId;
}
async initialize() {
Utils.setPSModulePath();
const azLatestVersion: string = await Utils.getLatestModule(Constants.moduleName);
core.debug(`Az Module version used: ${azLatestVersion}`);
Utils.setPSModulePath(`${Constants.prefix}${azLatestVersion}`);
}
async login() {
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);
}
}

View File

@@ -0,0 +1,20 @@
import * as io from '@actions/io';
import * as exec from '@actions/exec';
export default class PowerShellToolRunner {
static psPath: string;
static async init() {
if(!PowerShellToolRunner.psPath) {
PowerShellToolRunner.psPath = await io.which("pwsh", true);
}
}
static async executePowerShellCommand(command: string, options: any = {}) {
try {
await exec.exec(`"${PowerShellToolRunner.psPath}" -Command "${command}"`, [], options);
} catch(error) {
throw new Error(error);
}
}
}

View File

@@ -0,0 +1,16 @@
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};`;
}
}
this.script += `Get-AzContext`;
return this.script;
}
}

View File

@@ -0,0 +1,55 @@
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<string> {
let output: string = "";
let error: string = "";
const options: any = {
listeners: {
stdout: (data: Buffer) => {
output += data.toString();
},
stderr: (data: Buffer) => {
error += data.toString();
}
}
};
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();
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}`;
}
}

View File

@@ -1,76 +0,0 @@
import * as os from 'os';
import * as core from '@actions/core';
import * as exec from '@actions/exec';
import * as io from '@actions/io';
var psPath: string;
export const initializeAz = async (servicePrincipalId: string, servicePrincipalKey: string, tenantId: string, subscriptionId: string) => {
psPath = await io.which("pwsh", true);
await importModule();
await loginToAzure(servicePrincipalId, servicePrincipalKey, tenantId, subscriptionId);
}
async function importModule() {
setPSModulePath();
const prefix = "az_";
const moduleName: string = "Az.Accounts";
const azLatestVersion: string = await getLatestModule(moduleName);
core.debug(`Az Module version used: ${azLatestVersion}`);
setPSModulePath(`${prefix}${azLatestVersion}`);
}
function setPSModulePath(azPSVersion: string = "") {
let modulePath: string = "";
const RUNNER: string = 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;
}
process.env.PSModulePath = `${modulePath}${process.env.PSModulePath}`;
}
async function getLatestModule(moduleName: string) {
let output: string = "";
let error: string = "";
let options: any = {
listeners: {
stdout: (data: Buffer) => {
output += data.toString();
},
stderr: (data: Buffer) => {
error += data.toString();
}
}
};
await executePowerShellCommand(`(Get-Module -Name ${moduleName} -ListAvailable | Sort-Object Version -Descending | Select-Object -First 1).Version.ToString()`, options);
return output.trim();
}
async function loginToAzure(servicePrincipalId: string, servicePrincipalKey: string, tenantId: string, subscriptionId: string) {
const environment: string = "AzureCloud";
await executePowerShellCommand(`Clear-AzContext -Scope Process`);
await executePowerShellCommand(`Clear-AzContext -Scope CurrentUser -Force -ErrorAction SilentlyContinue`);
await executePowerShellCommand(`Connect-AzAccount -ServicePrincipal -Tenant ${tenantId} -Credential \
(New-Object System.Management.Automation.PSCredential('${servicePrincipalId}',(ConvertTo-SecureString ${servicePrincipalKey} -AsPlainText -Force))) \
-Environment ${environment}`);
await executePowerShellCommand(`Set-AzContext -SubscriptionId ${subscriptionId} -TenantId ${tenantId}`);
await executePowerShellCommand(`Get-AzContext`);
}
async function executePowerShellCommand(command: string, options: any = {}) {
try {
await exec.exec(`"${psPath}" -Command "${command}"`, [], options);
} catch(error) {
throw new Error(error);
}
}

View File

@@ -4,7 +4,7 @@ import * as exec from '@actions/exec';
import * as io from '@actions/io'; import * as io from '@actions/io';
import { FormatType, SecretParser } from 'actions-secret-parser'; import { FormatType, SecretParser } from 'actions-secret-parser';
import { initializeAz } from './loginAzurePowerShell'; import { ServicePrincipalLogin } from './ServicePrincipalLogin';
var azPath: string; var azPath: string;
var prefix = !!process.env.AZURE_HTTP_USER_AGENT ? `${process.env.AZURE_HTTP_USER_AGENT}` : ""; var prefix = !!process.env.AZURE_HTTP_USER_AGENT ? `${process.env.AZURE_HTTP_USER_AGENT}` : "";
@@ -26,12 +26,18 @@ async function main() {
let servicePrincipalKey = secrets.getSecret("$.clientSecret", true); let servicePrincipalKey = secrets.getSecret("$.clientSecret", true);
let tenantId = secrets.getSecret("$.tenantId", false); let tenantId = secrets.getSecret("$.tenantId", false);
let subscriptionId = secrets.getSecret("$.subscriptionId", false); let subscriptionId = secrets.getSecret("$.subscriptionId", false);
const enablePSSession = !!core.getInput('enable-PSSession');
if (!servicePrincipalId || !servicePrincipalKey || !tenantId || !subscriptionId) { 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."); 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(`login --service-principal -u "${servicePrincipalId}" -p "${servicePrincipalKey}" --tenant "${tenantId}"`);
await executeAzCliCommand(`account set --subscription "${subscriptionId}"`); await executeAzCliCommand(`account set --subscription "${subscriptionId}"`);
await initializeAz(servicePrincipalId, servicePrincipalKey, tenantId, subscriptionId); if (enablePSSession) {
console.log(`Running Azure PS Login`);
const spnlogin: ServicePrincipalLogin = new ServicePrincipalLogin(servicePrincipalId, servicePrincipalKey, tenantId, subscriptionId);
spnlogin.initialize();
spnlogin.login();
}
console.log("Login successful."); console.log("Login successful.");
} catch (error) { } catch (error) {
core.error("Login failed. Please check the credentials. For more information refer https://aka.ms/create-secrets-for-GitHub-workflows"); core.error("Login failed. Please check the credentials. For more information refer https://aka.ms/create-secrets-for-GitHub-workflows");