Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -305,8 +305,9 @@ public static async Task<bool> ProbeImdsEndpointAsync(
}
catch (Exception ex)
{
requestContext.Logger.Info($"[Managed Identity] {imdsStringHelper} probe endpoint failure. Exception occurred while sending request to probe endpoint: {ex}");
return false;
throw new MsalServiceException(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about this:

  1. Let cancellation exceptions pass through
  2. Capture the messages why IMDS1 and IMDS2 probe failed and output them when app calls AcquireToken() . Smth like "Cannot acquire a token for managed identity. IMDSv1 discovery failed with xyz and IMDSv2 discovery failed with bar"

Copy link
Member

@bgavrilMS bgavrilMS Dec 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@christothes - do you need the new GetSourceAsync API to also return to you why IMDSv1 and IMDSv2 probes failed when we return Source.None ?

MsalError.ImdsServiceError,
$"[Managed Identity] {imdsStringHelper} probe endpoint failure. Exception occurred while sending request to probe endpoint: {ex}");
}

// probe omits the "Metadata: true" header and then treats 400 Bad Request as success
Expand Down
5 changes: 5 additions & 0 deletions src/client/Microsoft.Identity.Client/MsalError.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1232,5 +1232,10 @@ public static class MsalError
/// All managed identity sources are unavailable.
/// </summary>
public const string ManagedIdentityAllSourcesUnavailable = "managed_identity_all_sources_unavailable";

/// <summary>
/// Represents the error code returned when an IMDS operation fails.
/// </summary>
public const string ImdsServiceError = "imds_service_error";
}
}
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
const Microsoft.Identity.Client.MsalError.ManagedIdentityAllSourcesUnavailable = "managed_identity_all_sources_unavailable" -> string
const Microsoft.Identity.Client.MsalError.ImdsServiceError = "imds_service_error" -> string
const Microsoft.Identity.Client.MsalError.ManagedIdentityAllSourcesUnavailable = "managed_identity_all_sources_unavailable" -> string
Microsoft.Identity.Client.ManagedIdentityApplication.GetManagedIdentitySourceAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource>
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
const Microsoft.Identity.Client.MsalError.ManagedIdentityAllSourcesUnavailable = "managed_identity_all_sources_unavailable" -> string
const Microsoft.Identity.Client.MsalError.ImdsServiceError = "imds_service_error" -> string
const Microsoft.Identity.Client.MsalError.ManagedIdentityAllSourcesUnavailable = "managed_identity_all_sources_unavailable" -> string
Microsoft.Identity.Client.ManagedIdentityApplication.GetManagedIdentitySourceAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource>
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
const Microsoft.Identity.Client.MsalError.ManagedIdentityAllSourcesUnavailable = "managed_identity_all_sources_unavailable" -> string
const Microsoft.Identity.Client.MsalError.ImdsServiceError = "imds_service_error" -> string
const Microsoft.Identity.Client.MsalError.ManagedIdentityAllSourcesUnavailable = "managed_identity_all_sources_unavailable" -> string
Microsoft.Identity.Client.ManagedIdentityApplication.GetManagedIdentitySourceAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource>
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
const Microsoft.Identity.Client.MsalError.ManagedIdentityAllSourcesUnavailable = "managed_identity_all_sources_unavailable" -> string
const Microsoft.Identity.Client.MsalError.ImdsServiceError = "imds_service_error" -> string
const Microsoft.Identity.Client.MsalError.ManagedIdentityAllSourcesUnavailable = "managed_identity_all_sources_unavailable" -> string
Microsoft.Identity.Client.ManagedIdentityApplication.GetManagedIdentitySourceAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource>
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
const Microsoft.Identity.Client.MsalError.ManagedIdentityAllSourcesUnavailable = "managed_identity_all_sources_unavailable" -> string
const Microsoft.Identity.Client.MsalError.ImdsServiceError = "imds_service_error" -> string
const Microsoft.Identity.Client.MsalError.ManagedIdentityAllSourcesUnavailable = "managed_identity_all_sources_unavailable" -> string
Microsoft.Identity.Client.ManagedIdentityApplication.GetManagedIdentitySourceAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource>
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
const Microsoft.Identity.Client.MsalError.ManagedIdentityAllSourcesUnavailable = "managed_identity_all_sources_unavailable" -> string
const Microsoft.Identity.Client.MsalError.ImdsServiceError = "imds_service_error" -> string
const Microsoft.Identity.Client.MsalError.ManagedIdentityAllSourcesUnavailable = "managed_identity_all_sources_unavailable" -> string
Microsoft.Identity.Client.ManagedIdentityApplication.GetManagedIdentitySourceAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource>
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

using System;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Identity.Client;
Expand Down Expand Up @@ -436,7 +435,7 @@ await mi.AcquireTokenForManagedIdentity(ManagedIdentityTests.Resource)
}

[TestMethod]
public async Task ProbeImdsEndpointAsync_TimesOutAfterOneSecond()
public async Task ProbeImdsV1EndpointAsync_TimesOutAfterOneSecond()
{
using (new EnvVariableContext())
using (var httpManager = new MockHttpManager())
Expand All @@ -450,19 +449,19 @@ public async Task ProbeImdsEndpointAsync_TimesOutAfterOneSecond()
var managedIdentityApp = miBuilder.Build();

httpManager.AddMockHandler(MockHelpers.MockImdsProbeFailure(ImdsVersion.V2));
httpManager.AddMockHandler(MockHelpers.MockImdsProbe(ImdsVersion.V1));
// ImdsV1 mock is not needed, as the request will not be sent due to cancellation token being cancelled

var imdsProbesCancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(0)).Token; // timeout immediately

var miSource = await (managedIdentityApp as ManagedIdentityApplication).GetManagedIdentitySourceAsync(imdsProbesCancellationToken).ConfigureAwait(false);
Assert.AreEqual(ManagedIdentitySource.None, miSource); // Probe timed out, no source available
var cts = new CancellationTokenSource();
cts.Cancel();
var imdsProbesCancellationToken = cts.Token;

var ex = await Assert.ThrowsExceptionAsync<MsalClientException>(async () =>
await managedIdentityApp.AcquireTokenForManagedIdentity(ManagedIdentityTests.Resource)
.ExecuteAsync().ConfigureAwait(false)
).ConfigureAwait(false);
var ex =
await Assert.ThrowsExceptionAsync<MsalServiceException>(async () =>
await (managedIdentityApp as ManagedIdentityApplication).GetManagedIdentitySourceAsync(imdsProbesCancellationToken)
.ConfigureAwait(false))
.ConfigureAwait(false);

Assert.AreEqual(MsalError.ManagedIdentityAllSourcesUnavailable, ex.ErrorCode);
Assert.AreEqual(MsalError.ImdsServiceError, ex.ErrorCode);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,36 @@ public async Task ProbeImdsEndpointAsyncFails404WhichIsNonRetriableAndRetryPolic
Assert.AreEqual(ManagedIdentitySource.Imds, miSource);
}
}

[TestMethod]
public async Task ProbeImdsV2EndpointAsync_TimesOutAfterOneSecond()
{
using (new EnvVariableContext())
using (var httpManager = new MockHttpManager())
{
var miBuilder = ManagedIdentityApplicationBuilder.Create(ManagedIdentityId.SystemAssigned);

miBuilder
.WithHttpManager(httpManager)
.WithRetryPolicyFactory(_testRetryPolicyFactory);

var managedIdentityApp = miBuilder.Build();

// ImdsV2 mock is not needed, as the request will not be sent due to cancellation token being cancelled

var cts = new CancellationTokenSource();
cts.Cancel();
var imdsProbesCancellationToken = cts.Token;

var ex =
await Assert.ThrowsExceptionAsync<MsalServiceException>(async () =>
await (managedIdentityApp as ManagedIdentityApplication).GetManagedIdentitySourceAsync(imdsProbesCancellationToken)
.ConfigureAwait(false))
.ConfigureAwait(false);

Assert.AreEqual(MsalError.ImdsServiceError, ex.ErrorCode);
}
}
#endregion Probe Tests

#region Fallback Behavior Tests
Expand Down
Loading