in src/commands/utils/clusters.ts [216:290]
async function getAadKubeconfig(
client: azcs.ContainerServiceClient,
resourceGroup: string,
clusterName: string,
): Promise<Errorable<string>> {
let clusterUserCredentials: azcs.CredentialResults;
try {
// For AAD clusters, force the credentials to be in 'exec' format.
clusterUserCredentials = await client.managedClusters.listClusterUserCredentials(resourceGroup, clusterName, {
format: azcs.KnownFormat.Exec,
});
} catch (e) {
return {
succeeded: false,
error: `Failed to retrieve user credentials for AAD cluster ${clusterName}: ${e}`,
};
}
// The initial credentials contain an 'exec' section that calls kubelogin with devicecode authentication.
const unauthenticatedKubeconfig = getClusterUserKubeconfig(clusterUserCredentials, clusterName);
if (failed(unauthenticatedKubeconfig)) {
return unauthenticatedKubeconfig;
}
// Parse the kubeconfig YAML into an object so that we can read and update exec options.
let kubeconfigYaml: KubeConfig;
try {
kubeconfigYaml = yaml.load(unauthenticatedKubeconfig.result) as KubeConfig;
} catch (e) {
return { succeeded: false, error: `Failed to parse kubeconfig YAML for ${clusterName}: ${e}` };
}
// We expect there will be just one user in the kubeconfig. Read the 'exec' arguments for it.
const execBlock = kubeconfigYaml.users[0].user.exec;
const execOptions = readExecOptions(execBlock.args);
// Get a token whose audience is the AKS server ID.
const scopes = [`${execOptions.serverId}/.default`, `VSCODE_TENANT:${execOptions.tenantId}`];
let session: AuthenticationSession;
try {
session = await authentication.getSession("microsoft", scopes, { createIfNone: true });
} catch (e) {
return {
succeeded: false,
error: `Failed to retrieve Microsoft authentication session for scopes [${scopes.join(",")}]: ${getErrorMessage(e)}`,
};
}
const tokenInfo = getTokenInfo(session);
if (failed(tokenInfo)) {
return tokenInfo;
}
// Now we have a token the user can use to access their cluster, but kubelogin doesn't support that being passed as an
// argument directly. Instead, we can save it to a temp directory and instruct kubelogin to use that as its cache directory.
const cacheDir = storeCachedAadToken(tokenInfo.result, execOptions);
if (failed(cacheDir)) {
return cacheDir;
}
// This extension controls the version of kubelogin used, so that:
// 1. We don't need to rely on the user having previously downloaded it, and
// 2. This workflow doesn't get broken by kubelogin behaviour changes between versions
const kubeloginPath = await getKubeloginBinaryPath();
if (failed(kubeloginPath)) {
return kubeloginPath;
}
// Update the kubeconfig YAML with the new kubelogin options, and return the serialized output as a string.
execBlock.command = kubeloginPath.result;
execBlock.args = buildExecOptionsWithCache(execOptions, cacheDir.result);
const authenticatedKubeConfig = yaml.dump(kubeconfigYaml);
return { succeeded: true, result: authenticatedKubeConfig };
}