diff --git a/sdk/identity/identity/CHANGELOG.md b/sdk/identity/identity/CHANGELOG.md index b7bb7acbdad7..e09c0804083f 100644 --- a/sdk/identity/identity/CHANGELOG.md +++ b/sdk/identity/identity/CHANGELOG.md @@ -1,9 +1,23 @@ # Release History +## 2.1.0-beta.1 (Unreleased) + +### Features Added + +- Added log warning for non-support of user assigned identity in Managed Identity credentials in Cloud Shell environments + +### Breaking Changes + +### Bugs Fixed + +### Other Changes + ## 2.0.3 (Unreleased) ### Features Added +- Added log warning for non-support of user assigned identity in Managed Identity credentials in Cloud Shell environments + ### Breaking Changes ### Bugs Fixed diff --git a/sdk/identity/identity/src/credentials/managedIdentityCredential/cloudShellMsi.ts b/sdk/identity/identity/src/credentials/managedIdentityCredential/cloudShellMsi.ts index a26e790508d6..fadc597a2780 100644 --- a/sdk/identity/identity/src/credentials/managedIdentityCredential/cloudShellMsi.ts +++ b/sdk/identity/identity/src/credentials/managedIdentityCredential/cloudShellMsi.ts @@ -12,7 +12,7 @@ import { MSI, MSIConfiguration } from "./models"; import { mapScopesToResource } from "./utils"; const msiName = "ManagedIdentityCredential - CloudShellMSI"; -const logger = credentialLogger(msiName); +export const logger = credentialLogger(msiName); /** * Generates the options used on the request for an access token. @@ -53,6 +53,7 @@ function prepareRequestOptions( /** * Defines how to determine whether the Azure Cloud Shell MSI is available, and also how to retrieve a token from the Azure Cloud Shell MSI. + * Since Azure Managed Identities aren't available in the Azure Cloud Shell, we log a warning for users that try to access cloud shell using user assigned identity. */ export const cloudShellMsi: MSI = { async isAvailable(scopes): Promise { @@ -61,6 +62,7 @@ export const cloudShellMsi: MSI = { logger.info(`${msiName}: Unavailable. Multiple scopes are not supported.`); return false; } + const result = Boolean(process.env.MSI_ENDPOINT); if (!result) { logger.info(`${msiName}: Unavailable. The environment variable MSI_ENDPOINT is needed.`); @@ -73,6 +75,11 @@ export const cloudShellMsi: MSI = { ): Promise { const { identityClient, scopes, clientId } = configuration; + if (clientId) { + logger.warning( + `${msiName}: does not support user-assigned identities in the Cloud Shell environment. Argument clientId will be ignored.` + ); + } logger.info( `${msiName}: Using the endpoint coming form the environment variable MSI_ENDPOINT = ${process.env.MSI_ENDPOINT}.` ); diff --git a/sdk/identity/identity/src/util/logging.ts b/sdk/identity/identity/src/util/logging.ts index 85bb44c9d47a..081164a6cb8b 100644 --- a/sdk/identity/identity/src/util/logging.ts +++ b/sdk/identity/identity/src/util/logging.ts @@ -69,6 +69,7 @@ export interface CredentialLoggerInstance { title: string; fullTitle: string; info(message: string): void; + warning(message: string): void; /** * The logging functions for warning and error are intentionally left out, since we want the identity logging to be at the info level. * Otherwise, they would look like: @@ -97,10 +98,14 @@ export function credentialLoggerInstance( log.info(`${fullTitle} =>`, message); } + function warning(message: string): void { + log.warning(`${fullTitle} =>`, message); + } return { title, fullTitle, info, + warning, }; } diff --git a/sdk/identity/identity/test/internal/node/managedIdentityCredential.spec.ts b/sdk/identity/identity/test/internal/node/managedIdentityCredential.spec.ts index fbce216678b6..567cf02570f3 100644 --- a/sdk/identity/identity/test/internal/node/managedIdentityCredential.spec.ts +++ b/sdk/identity/identity/test/internal/node/managedIdentityCredential.spec.ts @@ -7,6 +7,7 @@ import { tmpdir } from "os"; import { mkdtempSync, rmdirSync, unlinkSync, writeFileSync } from "fs"; import { RestError } from "@azure/core-rest-pipeline"; import { ManagedIdentityCredential } from "../../../src"; +import Sinon from "sinon"; import { imdsHost, imdsApiVersion, @@ -19,6 +20,9 @@ import { import { createResponse, IdentityTestContextInterface } from "../../httpRequestsCommon"; import { IdentityTestContext } from "../../httpRequests"; import { AzureAuthorityHosts, DefaultAuthorityHost, DefaultTenantId } from "../../../src/constants"; +import { setLogLevel } from "@azure/logger"; +import { logger } from "../../../src/credentials/managedIdentityCredential/cloudShellMsi"; +import { Context } from "mocha"; describe("ManagedIdentityCredential", function () { let testContext: IdentityTestContextInterface; @@ -360,18 +364,35 @@ describe("ManagedIdentityCredential", function () { it("sends an authorization request correctly in an Cloud Shell environment", async () => { // Trigger Cloud Shell behavior by setting environment variables process.env.MSI_ENDPOINT = "https://endpoint"; - const authDetails = await testContext.sendCredentialRequests({ scopes: ["https://service/.default"], - credential: new ManagedIdentityCredential("client"), + credential: new ManagedIdentityCredential(), secureResponses: [createResponse(200, { access_token: "token" })], }); - const authRequest = authDetails.requests[0]; assert.equal(authRequest.method, "POST"); assert.equal(authDetails.result!.token, "token"); }); + it("authorization request fails with client id passed in an Cloud Shell environment", async function (this: Context) { + // Trigger Cloud Shell behavior by setting environment variables + process.env.MSI_ENDPOINT = "https://endpoint"; + const msiGetTokenSpy = Sinon.spy(ManagedIdentityCredential.prototype, "getToken"); + const loggerSpy = Sinon.spy(logger, "warning"); + setLogLevel("warning"); + const authDetails = await testContext.sendCredentialRequests({ + scopes: ["https://service/.default"], + credential: new ManagedIdentityCredential("client"), + secureResponses: [createResponse(200, { access_token: "token" })], + }); + assert.equal(authDetails.result!.token, "token"); + assert.equal(msiGetTokenSpy.called, true); + assert.equal(loggerSpy.calledOnce, true); + assert.deepEqual(loggerSpy.args[0], [ + "ManagedIdentityCredential - CloudShellMSI: does not support user-assigned identities in the Cloud Shell environment. Argument clientId will be ignored.", + ]); + }); + it("sends an authorization request correctly in an Azure Arc environment", async function (this: Mocha.Context) { // Trigger Azure Arc behavior by setting environment variables