mirror of
https://github.com/azure/login.git
synced 2026-06-08 21:17:07 +00:00
* pre and post cleanup-cli * exec azpath * enable azure powershell part * set user agent * extract utils * divide cleanup * extract azpsconfig class * fix test * move runpsscript * change to AzPSUtils * fix typo
169 lines
6.9 KiB
TypeScript
169 lines
6.9 KiB
TypeScript
import * as exec from '@actions/exec';
|
|
import { LoginConfig } from "../common/LoginConfig";
|
|
import { ExecOptions } from '@actions/exec/lib/interfaces';
|
|
import * as core from '@actions/core';
|
|
import * as io from '@actions/io';
|
|
|
|
export class AzureCliLogin {
|
|
loginConfig: LoginConfig;
|
|
azPath: string;
|
|
loginOptions: ExecOptions;
|
|
|
|
constructor(loginConfig: LoginConfig) {
|
|
this.loginConfig = loginConfig;
|
|
this.loginOptions = defaultExecOptions();
|
|
}
|
|
|
|
async login() {
|
|
core.info(`Running Azure CLI Login.`);
|
|
this.azPath = await io.which("az", true);
|
|
core.debug(`Azure CLI path: ${this.azPath}`);
|
|
|
|
let output: string = "";
|
|
const execOptions: any = {
|
|
listeners: {
|
|
stdout: (data: Buffer) => {
|
|
output += data.toString();
|
|
}
|
|
}
|
|
};
|
|
|
|
await this.executeAzCliCommand(["--version"], true, execOptions);
|
|
core.debug(`Azure CLI version used:\n${output}`);
|
|
|
|
await this.executeAzCliCommand(["account", "clear"], true, execOptions);
|
|
|
|
this.setAzurestackEnvIfNecessary();
|
|
|
|
await this.executeAzCliCommand(["cloud", "set", "-n", this.loginConfig.environment], false);
|
|
core.info(`Done setting cloud: "${this.loginConfig.environment}"`);
|
|
|
|
if (this.loginConfig.authType === LoginConfig.AUTH_TYPE_SERVICE_PRINCIPAL) {
|
|
let args = ["--service-principal",
|
|
"--username", this.loginConfig.servicePrincipalId,
|
|
"--tenant", this.loginConfig.tenantId
|
|
];
|
|
if (this.loginConfig.servicePrincipalSecret) {
|
|
await this.loginWithSecret(args);
|
|
}
|
|
else {
|
|
await this.loginWithOIDC(args);
|
|
}
|
|
}
|
|
else {
|
|
let args = ["--identity"];
|
|
if (this.loginConfig.servicePrincipalId) {
|
|
await this.loginWithUserAssignedIdentity(args);
|
|
}
|
|
else {
|
|
await this.loginWithSystemAssignedIdentity(args);
|
|
}
|
|
}
|
|
}
|
|
|
|
async setAzurestackEnvIfNecessary() {
|
|
if (this.loginConfig.environment != "azurestack") {
|
|
return;
|
|
}
|
|
if (!this.loginConfig.resourceManagerEndpointUrl) {
|
|
throw new Error("resourceManagerEndpointUrl is a required parameter when environment is defined.");
|
|
}
|
|
|
|
core.info(`Unregistering cloud: "${this.loginConfig.environment}" first if it exists`);
|
|
try {
|
|
await this.executeAzCliCommand(["cloud", "set", "-n", "AzureCloud"], true);
|
|
await this.executeAzCliCommand(["cloud", "unregister", "-n", this.loginConfig.environment], false);
|
|
}
|
|
catch (error) {
|
|
core.info(`Ignore cloud not registered error: "${error}"`);
|
|
}
|
|
|
|
core.info(`Registering cloud: "${this.loginConfig.environment}" with ARM endpoint: "${this.loginConfig.resourceManagerEndpointUrl}"`);
|
|
try {
|
|
let baseUri = this.loginConfig.resourceManagerEndpointUrl;
|
|
if (baseUri.endsWith('/')) {
|
|
baseUri = baseUri.substring(0, baseUri.length - 1); // need to remove trailing / from resourceManagerEndpointUrl to correctly derive suffixes below
|
|
}
|
|
let suffixKeyvault = ".vault" + baseUri.substring(baseUri.indexOf('.')); // keyvault suffix starts with .
|
|
let suffixStorage = baseUri.substring(baseUri.indexOf('.') + 1); // storage suffix starts without .
|
|
let profileVersion = "2019-03-01-hybrid";
|
|
await this.executeAzCliCommand(["cloud", "register", "-n", this.loginConfig.environment, "--endpoint-resource-manager", `"${this.loginConfig.resourceManagerEndpointUrl}"`, "--suffix-keyvault-dns", `"${suffixKeyvault}"`, "--suffix-storage-endpoint", `"${suffixStorage}"`, "--profile", `"${profileVersion}"`], false);
|
|
}
|
|
catch (error) {
|
|
core.error(`Error while trying to register cloud "${this.loginConfig.environment}"`);
|
|
throw error;
|
|
}
|
|
|
|
core.info(`Done registering cloud: "${this.loginConfig.environment}"`)
|
|
}
|
|
|
|
async loginWithSecret(args: string[]) {
|
|
core.info("Note: Azure/login action also supports OIDC login mechanism. Refer https://github.com/azure/login#configure-a-service-principal-with-a-federated-credential-to-use-oidc-based-authentication for more details.")
|
|
args.push(`--password=${this.loginConfig.servicePrincipalSecret}`);
|
|
await this.callCliLogin(args, 'service principal with secret');
|
|
}
|
|
|
|
async loginWithOIDC(args: string[]) {
|
|
await this.loginConfig.getFederatedToken();
|
|
args.push("--federated-token", this.loginConfig.federatedToken);
|
|
await this.callCliLogin(args, 'OIDC');
|
|
}
|
|
|
|
async loginWithUserAssignedIdentity(args: string[]) {
|
|
args.push("--username", this.loginConfig.servicePrincipalId);
|
|
await this.callCliLogin(args, 'user-assigned managed identity');
|
|
}
|
|
|
|
async loginWithSystemAssignedIdentity(args: string[]) {
|
|
await this.callCliLogin(args, 'system-assigned managed identity');
|
|
}
|
|
|
|
async callCliLogin(args: string[], methodName: string) {
|
|
core.info(`Attempting Azure CLI login by using ${methodName}...`);
|
|
args.unshift("login");
|
|
if (this.loginConfig.allowNoSubscriptionsLogin) {
|
|
args.push("--allow-no-subscriptions");
|
|
}
|
|
await this.executeAzCliCommand(args, true, this.loginOptions);
|
|
if (this.loginConfig.subscriptionId) {
|
|
await this.setSubscription();
|
|
}
|
|
core.info(`Azure CLI login succeeds by using ${methodName}.`);
|
|
}
|
|
|
|
async setSubscription() {
|
|
let args = ["account", "set", "--subscription", this.loginConfig.subscriptionId];
|
|
await this.executeAzCliCommand(args, true, this.loginOptions);
|
|
core.info("Subscription is set successfully.");
|
|
}
|
|
|
|
async executeAzCliCommand(
|
|
args: string[],
|
|
silent?: boolean,
|
|
execOptions: any = {}) {
|
|
execOptions.silent = !!silent;
|
|
await exec.exec(`"${this.azPath}"`, args, execOptions);
|
|
}
|
|
}
|
|
|
|
function defaultExecOptions(): exec.ExecOptions {
|
|
return {
|
|
silent: true,
|
|
listeners: {
|
|
stderr: (data: Buffer) => {
|
|
let error = data.toString();
|
|
let startsWithWarning = error.toLowerCase().startsWith('warning');
|
|
let startsWithError = error.toLowerCase().startsWith('error');
|
|
// printing ERROR
|
|
if (error && error.trim().length !== 0 && !startsWithWarning) {
|
|
if (startsWithError) {
|
|
//removing the keyword 'ERROR' to avoid duplicates while throwing error
|
|
error = error.slice(7);
|
|
}
|
|
core.error(error);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|