mirror of
https://github.com/azure/login.git
synced 2026-03-15 09:20:56 -04:00
187 lines
7.6 KiB
TypeScript
187 lines
7.6 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;
|
|
azVersion: string;
|
|
|
|
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}`);
|
|
try {
|
|
this.azVersion = JSON.parse(output)["azure-cli"];
|
|
}
|
|
catch (error) {
|
|
core.warning("Failed to parse Azure CLI version.");
|
|
}
|
|
await this.registerAzurestackEnvIfNecessary();
|
|
|
|
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 registerAzurestackEnvIfNecessary() {
|
|
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[]) {
|
|
let azcliMinorVersion = 0;
|
|
try {
|
|
azcliMinorVersion = parseInt(this.azVersion.split('.')[1], 10);
|
|
}
|
|
catch (error) {
|
|
core.warning("Failed to parse the minor version of Azure CLI. Assuming the version is less than 2.69.0");
|
|
}
|
|
//From Azure-cli v2.69.0, `--username` is replaced with `--client-id`, `--object-id` or `--resource-id`: https://github.com/Azure/azure-cli/pull/30525
|
|
if (azcliMinorVersion < 69) {
|
|
args.push("--username", this.loginConfig.servicePrincipalId);
|
|
}
|
|
else {
|
|
args.push("--client-id", 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);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|