DAC: Disable probe when MIC is selected via env var (#6755)

* DAC: Disable probe when MIC is selected via env var

* Fix missing newline at end of default_azure_credential_test.cpp

* Update sdk/identity/azure-identity/test/ut/managed_identity_credential_test.cpp

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update sdk/identity/azure-identity/test/ut/default_azure_credential_test.cpp

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Clang fix

* Make options always the last parameter

* Clang-format

* Add const char overload

* MIC: do not send probe request by default, unless it is used in DAC, unless DAC has MIC only selected via env var

* Rename isProbeEnabled to useProbeRequest, make options the last arg even in internal APIs

* Include the rest of the files

* Fix

* only expose UseProbeRequest in options

* Clang-format

---------

Co-authored-by: Anton Kolesnyk <antkmsft@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Anton Kolesnyk 2025-09-30 16:13:59 -07:00 committed by GitHub
parent 313a43af4f
commit ead279936d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 268 additions and 35 deletions

View File

@ -4,6 +4,10 @@
### Features Added ### Features Added
- Added `UseProbeRequest` option for `ManagedIdentityCredential`.
- By default, `ManagedIdentityCredential` does not send a probe request, unless it is a part of credential chain in `DefaultAzureCredential`.
- When `AZURE_TOKEN_CREDENTIALS` environment variable is configured to `ManagedIdentityCredential`, the `DefaultAzureCredential` does not issue a probe request and performs retries with exponential backoff.
### Breaking Changes ### Breaking Changes
### Bugs Fixed ### Bugs Fixed

View File

@ -168,6 +168,18 @@ namespace Azure { namespace Identity {
* it was configured. * it was configured.
*/ */
ManagedIdentityId IdentityId; ManagedIdentityId IdentityId;
/**
* @brief If Azure Instance Metadata Service (IMDS) gets selected as managed identity source,
* specifies whether the first request should be a short probe request (`true`), instead of a
* normal request with retries and exponential backoff (`false`). Default is `false`.
*
* @note When `true`, there's a potential that the credential would not detect IMDS being
* available on a machine, if the response was not received fast enough. When `false` and IMDS
* is not available, credential creation may take tens of seconds until multiple attempts to get
* a response from IMDS would fail.
*/
bool UseProbeRequest = false;
}; };
/** /**
@ -181,6 +193,11 @@ namespace Azure { namespace Identity {
private: private:
std::unique_ptr<_detail::ManagedIdentitySource> m_managedIdentitySource; std::unique_ptr<_detail::ManagedIdentitySource> m_managedIdentitySource;
explicit ManagedIdentityCredential(
std::string const& clientId,
bool useProbeRequest,
Core::Credentials::TokenCredentialOptions const& options);
public: public:
/** /**
* @brief Destructs `%TokenCredential`. * @brief Destructs `%TokenCredential`.
@ -196,8 +213,8 @@ namespace Azure { namespace Identity {
*/ */
explicit ManagedIdentityCredential( explicit ManagedIdentityCredential(
std::string const& clientId = std::string(), std::string const& clientId = std::string(),
Azure::Core::Credentials::TokenCredentialOptions const& options Core::Credentials::TokenCredentialOptions const& options
= Azure::Core::Credentials::TokenCredentialOptions()); = Core::Credentials::TokenCredentialOptions());
/** /**
* @brief Constructs a Managed Identity Credential. * @brief Constructs a Managed Identity Credential.
@ -212,8 +229,7 @@ namespace Azure { namespace Identity {
* *
* @param options Options for token retrieval. * @param options Options for token retrieval.
*/ */
explicit ManagedIdentityCredential( explicit ManagedIdentityCredential(Core::Credentials::TokenCredentialOptions const& options);
Azure::Core::Credentials::TokenCredentialOptions const& options);
/** /**
* @brief Gets an authentication token. * @brief Gets an authentication token.

View File

@ -69,25 +69,6 @@ DefaultAzureCredential::DefaultAzureCredential(
Create; Create;
}; };
static const std::array<CredentialInfo, 4> credentials = {
CredentialInfo{
true,
"EnvironmentCredential",
[](auto options) { return std::make_shared<EnvironmentCredential>(options); }},
CredentialInfo{
true,
"WorkloadIdentityCredential",
[](auto options) { return std::make_shared<WorkloadIdentityCredential>(options); }},
CredentialInfo{
true,
"ManagedIdentityCredential",
[](auto options) { return std::make_shared<ManagedIdentityCredential>(options); }},
CredentialInfo{
false,
"AzureCliCredential",
[](auto options) { return std::make_shared<AzureCliCredential>(options); }},
};
const auto envVarValue = Environment::GetVariable(CredentialSpecifierEnvVarName); const auto envVarValue = Environment::GetVariable(CredentialSpecifierEnvVarName);
const auto trimmedEnvVarValue = StringExtensions::Trim(envVarValue); const auto trimmedEnvVarValue = StringExtensions::Trim(envVarValue);
@ -99,6 +80,35 @@ DefaultAzureCredential::DefaultAzureCredential(
} }
bool specificCred = false; bool specificCred = false;
const std::array<CredentialInfo, 4> credentials = {
CredentialInfo{
true,
"EnvironmentCredential",
[](auto options) { return std::make_shared<EnvironmentCredential>(options); }},
CredentialInfo{
true,
"WorkloadIdentityCredential",
[](auto options) { return std::make_shared<WorkloadIdentityCredential>(options); }},
CredentialInfo{
true,
"ManagedIdentityCredential",
[&](auto options) {
// If specifically 'ManagedIdentityCredential' is used, do not perform a probe
// request, going for the full retry with exponential backoffs instead.
ManagedIdentityCredentialOptions managedIdentityCredentialOptions;
static_cast<Core::Credentials::TokenCredentialOptions&>(
managedIdentityCredentialOptions)
= options;
managedIdentityCredentialOptions.UseProbeRequest = !specificCred;
return std::make_shared<ManagedIdentityCredential>(managedIdentityCredentialOptions);
}},
CredentialInfo{
false,
"AzureCliCredential",
[](auto options) { return std::make_shared<AzureCliCredential>(options); }},
};
if (!trimmedEnvVarValue.empty()) if (!trimmedEnvVarValue.empty())
{ {
for (const auto& cred : credentials) for (const auto& cred : credentials)

View File

@ -14,6 +14,7 @@ std::unique_ptr<_detail::ManagedIdentitySource> CreateManagedIdentitySource(
std::string const& clientId, std::string const& clientId,
std::string const& objectId, std::string const& objectId,
std::string const& resourceId, std::string const& resourceId,
bool useProbeRequest,
Azure::Core::Credentials::TokenCredentialOptions const& options) Azure::Core::Credentials::TokenCredentialOptions const& options)
{ {
using namespace Azure::Core::Credentials; using namespace Azure::Core::Credentials;
@ -23,6 +24,7 @@ std::unique_ptr<_detail::ManagedIdentitySource> CreateManagedIdentitySource(
std::string const& clientId, std::string const& clientId,
std::string const& objectId, std::string const& objectId,
std::string const& resourceId, std::string const& resourceId,
bool useProbeRequest,
TokenCredentialOptions const& options) TokenCredentialOptions const& options)
= {AppServiceV2019ManagedIdentitySource::Create, = {AppServiceV2019ManagedIdentitySource::Create,
AppServiceV2017ManagedIdentitySource::Create, AppServiceV2017ManagedIdentitySource::Create,
@ -34,7 +36,8 @@ std::unique_ptr<_detail::ManagedIdentitySource> CreateManagedIdentitySource(
// For that reason, it is not possible to cover that execution branch in tests. // For that reason, it is not possible to cover that execution branch in tests.
for (auto create : managedIdentitySourceCreate) for (auto create : managedIdentitySourceCreate)
{ {
if (auto source = create(credentialName, clientId, objectId, resourceId, options)) if (auto source
= create(credentialName, clientId, objectId, resourceId, useProbeRequest, options))
{ {
return source; return source;
} }
@ -49,11 +52,19 @@ ManagedIdentityCredential::~ManagedIdentityCredential() = default;
ManagedIdentityCredential::ManagedIdentityCredential( ManagedIdentityCredential::ManagedIdentityCredential(
std::string const& clientId, std::string const& clientId,
bool useProbeRequest,
Azure::Core::Credentials::TokenCredentialOptions const& options) Azure::Core::Credentials::TokenCredentialOptions const& options)
: TokenCredential("ManagedIdentityCredential") : TokenCredential("ManagedIdentityCredential")
{ {
m_managedIdentitySource m_managedIdentitySource = CreateManagedIdentitySource(
= CreateManagedIdentitySource(GetCredentialName(), clientId, {}, {}, options); GetCredentialName(), clientId, {}, {}, useProbeRequest, options);
}
ManagedIdentityCredential::ManagedIdentityCredential(
std::string const& clientId,
Azure::Core::Credentials::TokenCredentialOptions const& options)
: ManagedIdentityCredential(clientId, false, options)
{
} }
ManagedIdentityCredential::ManagedIdentityCredential( ManagedIdentityCredential::ManagedIdentityCredential(
@ -64,20 +75,35 @@ ManagedIdentityCredential::ManagedIdentityCredential(
switch (idType) switch (idType)
{ {
case ManagedIdentityIdKind::SystemAssigned: case ManagedIdentityIdKind::SystemAssigned:
m_managedIdentitySource m_managedIdentitySource = CreateManagedIdentitySource(
= CreateManagedIdentitySource(GetCredentialName(), {}, {}, {}, options); GetCredentialName(), {}, {}, {}, options.UseProbeRequest, options);
break; break;
case ManagedIdentityIdKind::ClientId: case ManagedIdentityIdKind::ClientId:
m_managedIdentitySource = CreateManagedIdentitySource( m_managedIdentitySource = CreateManagedIdentitySource(
GetCredentialName(), options.IdentityId.GetId(), {}, {}, options); GetCredentialName(),
options.IdentityId.GetId(),
{},
{},
options.UseProbeRequest,
options);
break; break;
case ManagedIdentityIdKind::ObjectId: case ManagedIdentityIdKind::ObjectId:
m_managedIdentitySource = CreateManagedIdentitySource( m_managedIdentitySource = CreateManagedIdentitySource(
GetCredentialName(), {}, options.IdentityId.GetId(), {}, options); GetCredentialName(),
{},
options.IdentityId.GetId(),
{},
options.UseProbeRequest,
options);
break; break;
case ManagedIdentityIdKind::ResourceId: case ManagedIdentityIdKind::ResourceId:
m_managedIdentitySource = CreateManagedIdentitySource( m_managedIdentitySource = CreateManagedIdentitySource(
GetCredentialName(), {}, {}, options.IdentityId.GetId(), options); GetCredentialName(),
{},
{},
options.IdentityId.GetId(),
options.UseProbeRequest,
options);
break; break;
default: default:
throw std::invalid_argument( throw std::invalid_argument(
@ -88,7 +114,7 @@ ManagedIdentityCredential::ManagedIdentityCredential(
ManagedIdentityCredential::ManagedIdentityCredential( ManagedIdentityCredential::ManagedIdentityCredential(
Azure::Core::Credentials::TokenCredentialOptions const& options) Azure::Core::Credentials::TokenCredentialOptions const& options)
: ManagedIdentityCredential(std::string(), options) : ManagedIdentityCredential({}, false, options)
{ {
} }

View File

@ -263,8 +263,10 @@ std::unique_ptr<ManagedIdentitySource> AppServiceV2017ManagedIdentitySource::Cre
std::string const& clientId, std::string const& clientId,
std::string const& objectId, std::string const& objectId,
std::string const& resourceId, std::string const& resourceId,
bool useProbeRequest,
Core::Credentials::TokenCredentialOptions const& options) Core::Credentials::TokenCredentialOptions const& options)
{ {
static_cast<void>(useProbeRequest);
return AppServiceManagedIdentitySource::Create<AppServiceV2017ManagedIdentitySource>( return AppServiceManagedIdentitySource::Create<AppServiceV2017ManagedIdentitySource>(
credName, clientId, objectId, resourceId, options, "MSI_ENDPOINT", "MSI_SECRET", "2017"); credName, clientId, objectId, resourceId, options, "MSI_ENDPOINT", "MSI_SECRET", "2017");
} }
@ -274,8 +276,10 @@ std::unique_ptr<ManagedIdentitySource> AppServiceV2019ManagedIdentitySource::Cre
std::string const& clientId, std::string const& clientId,
std::string const& objectId, std::string const& objectId,
std::string const& resourceId, std::string const& resourceId,
bool useProbeRequest,
Core::Credentials::TokenCredentialOptions const& options) Core::Credentials::TokenCredentialOptions const& options)
{ {
static_cast<void>(useProbeRequest);
return AppServiceManagedIdentitySource::Create<AppServiceV2019ManagedIdentitySource>( return AppServiceManagedIdentitySource::Create<AppServiceV2019ManagedIdentitySource>(
credName, credName,
clientId, clientId,
@ -292,8 +296,10 @@ std::unique_ptr<ManagedIdentitySource> CloudShellManagedIdentitySource::Create(
std::string const& clientId, std::string const& clientId,
std::string const& objectId, std::string const& objectId,
std::string const& resourceId, std::string const& resourceId,
bool useProbeRequest,
Azure::Core::Credentials::TokenCredentialOptions const& options) Azure::Core::Credentials::TokenCredentialOptions const& options)
{ {
static_cast<void>(useProbeRequest);
using Azure::Core::Credentials::AuthenticationException; using Azure::Core::Credentials::AuthenticationException;
constexpr auto EndpointVarName = "MSI_ENDPOINT"; constexpr auto EndpointVarName = "MSI_ENDPOINT";
@ -370,8 +376,10 @@ std::unique_ptr<ManagedIdentitySource> AzureArcManagedIdentitySource::Create(
std::string const& clientId, std::string const& clientId,
std::string const& objectId, std::string const& objectId,
std::string const& resourceId, std::string const& resourceId,
bool useProbeRequest,
Azure::Core::Credentials::TokenCredentialOptions const& options) Azure::Core::Credentials::TokenCredentialOptions const& options)
{ {
static_cast<void>(useProbeRequest);
using Azure::Core::Credentials::AuthenticationException; using Azure::Core::Credentials::AuthenticationException;
constexpr auto EndpointVarName = "IDENTITY_ENDPOINT"; constexpr auto EndpointVarName = "IDENTITY_ENDPOINT";
@ -499,6 +507,7 @@ std::unique_ptr<ManagedIdentitySource> ImdsManagedIdentitySource::Create(
std::string const& clientId, std::string const& clientId,
std::string const& objectId, std::string const& objectId,
std::string const& resourceId, std::string const& resourceId,
bool useProbeRequest,
Azure::Core::Credentials::TokenCredentialOptions const& options) Azure::Core::Credentials::TokenCredentialOptions const& options)
{ {
const std::string ImdsName = "Azure Instance Metadata Service"; const std::string ImdsName = "Azure Instance Metadata Service";
@ -529,8 +538,8 @@ std::unique_ptr<ManagedIdentitySource> ImdsManagedIdentitySource::Create(
} }
imdsUrl.SetPath("metadata/identity/oauth2/token"); imdsUrl.SetPath("metadata/identity/oauth2/token");
return std::unique_ptr<ManagedIdentitySource>( return std::unique_ptr<ManagedIdentitySource>(new ImdsManagedIdentitySource(
new ImdsManagedIdentitySource(clientId, objectId, resourceId, imdsUrl, options)); clientId, objectId, resourceId, imdsUrl, useProbeRequest, options));
} }
ImdsManagedIdentitySource::ImdsManagedIdentitySource( ImdsManagedIdentitySource::ImdsManagedIdentitySource(
@ -538,6 +547,7 @@ ImdsManagedIdentitySource::ImdsManagedIdentitySource(
std::string const& objectId, std::string const& objectId,
std::string const& resourceId, std::string const& resourceId,
Azure::Core::Url const& imdsUrl, Azure::Core::Url const& imdsUrl,
bool useProbeRequest,
Azure::Core::Credentials::TokenCredentialOptions const& options) Azure::Core::Credentials::TokenCredentialOptions const& options)
: ManagedIdentitySource(clientId, std::string(), options), : ManagedIdentitySource(clientId, std::string(), options),
m_request(Azure::Core::Http::HttpMethod::Get, imdsUrl) m_request(Azure::Core::Http::HttpMethod::Get, imdsUrl)
@ -569,7 +579,7 @@ ImdsManagedIdentitySource::ImdsManagedIdentitySource(
Core::Credentials::TokenCredentialOptions firstRequestOptions = options; Core::Credentials::TokenCredentialOptions firstRequestOptions = options;
firstRequestOptions.Retry.MaxRetries = 0; firstRequestOptions.Retry.MaxRetries = 0;
m_firstRequestPipeline = std::make_unique<TokenCredentialImpl>(firstRequestOptions); m_firstRequestPipeline = std::make_unique<TokenCredentialImpl>(firstRequestOptions);
m_firstRequestSucceeded = false; m_firstRequestSucceeded = !useProbeRequest;
} }
Azure::Core::Credentials::AccessToken ImdsManagedIdentitySource::GetToken( Azure::Core::Credentials::AccessToken ImdsManagedIdentitySource::GetToken(

View File

@ -113,6 +113,7 @@ namespace Azure { namespace Identity { namespace _detail {
std::string const& clientId, std::string const& clientId,
std::string const& objectId, std::string const& objectId,
std::string const& resourceId, std::string const& resourceId,
bool useProbeRequest,
Core::Credentials::TokenCredentialOptions const& options); Core::Credentials::TokenCredentialOptions const& options);
}; };
@ -146,6 +147,7 @@ namespace Azure { namespace Identity { namespace _detail {
std::string const& clientId, std::string const& clientId,
std::string const& objectId, std::string const& objectId,
std::string const& resourceId, std::string const& resourceId,
bool useProbeRequest,
Core::Credentials::TokenCredentialOptions const& options); Core::Credentials::TokenCredentialOptions const& options);
}; };
@ -164,6 +166,7 @@ namespace Azure { namespace Identity { namespace _detail {
std::string const& clientId, std::string const& clientId,
std::string const& objectId, std::string const& objectId,
std::string const& resourceId, std::string const& resourceId,
bool useProbeRequest,
Core::Credentials::TokenCredentialOptions const& options); Core::Credentials::TokenCredentialOptions const& options);
Core::Credentials::AccessToken GetToken( Core::Credentials::AccessToken GetToken(
@ -185,6 +188,7 @@ namespace Azure { namespace Identity { namespace _detail {
std::string const& clientId, std::string const& clientId,
std::string const& objectId, std::string const& objectId,
std::string const& resourceId, std::string const& resourceId,
bool useProbeRequest,
Core::Credentials::TokenCredentialOptions const& options); Core::Credentials::TokenCredentialOptions const& options);
Core::Credentials::AccessToken GetToken( Core::Credentials::AccessToken GetToken(
@ -204,6 +208,7 @@ namespace Azure { namespace Identity { namespace _detail {
std::string const& objectId, std::string const& objectId,
std::string const& resourceId, std::string const& resourceId,
Core::Url const& imdsUrl, Core::Url const& imdsUrl,
bool useProbeRequest,
Core::Credentials::TokenCredentialOptions const& options); Core::Credentials::TokenCredentialOptions const& options);
public: public:
@ -212,6 +217,7 @@ namespace Azure { namespace Identity { namespace _detail {
std::string const& clientId, std::string const& clientId,
std::string const& objectId, std::string const& objectId,
std::string const& resourceId, std::string const& resourceId,
bool useProbeRequest,
Core::Credentials::TokenCredentialOptions const& options); Core::Credentials::TokenCredentialOptions const& options);
Core::Credentials::AccessToken GetToken( Core::Credentials::AccessToken GetToken(

View File

@ -528,3 +528,96 @@ TEST(DefaultAzureCredential, RequireCredentialSpecifierEnvVarValue)
EXPECT_THROW( EXPECT_THROW(
static_cast<void>(std::make_unique<DefaultAzureCredential>(true)), AuthenticationException); static_cast<void>(std::make_unique<DefaultAzureCredential>(true)), AuthenticationException);
} }
TEST(DefaultAzureCredential, ImdsProbe)
{
using Azure::Core::Http::HttpStatusCode;
using Azure::Identity::Test::_detail::CredentialTestHelper;
constexpr auto ImATeapot = static_cast<HttpStatusCode>(418);
// AZURE_TOKEN_CREDENTIALS is set to "prod", which should result in ManagedIdentityCredential
// using useProbeRequest = true.
EXPECT_THROW(
static_cast<void>(CredentialTestHelper::SimulateTokenRequest(
[&ImATeapot](auto transport) {
TokenCredentialOptions options;
options.Transport.Transport = transport;
options.Retry.MaxRetries = 3;
options.Retry.RetryDelay = std::chrono::milliseconds(1);
options.Retry.StatusCodes.insert(ImATeapot);
CredentialTestHelper::EnvironmentOverride const env({
// Env vars are set to ensure that all the credential chain is inactive all the way
// up to ManagedIdentityCredential with IMDS source.
{"MSI_ENDPOINT", ""},
{"MSI_SECRET", ""},
{"IDENTITY_ENDPOINT", "https://visualstudio.com/"},
{"IMDS_ENDPOINT", ""},
{"IDENTITY_HEADER", ""},
{"IDENTITY_SERVER_THUMBPRINT", ""},
{"AZURE_POD_IDENTITY_AUTHORITY_HOST", ""},
{"AZURE_AUTHORITY_HOST", ""},
{"AZURE_TENANT_ID", ""},
{"AZURE_CLIENT_ID", ""},
{"AZURE_CLIENT_SECRET", ""},
{"AZURE_CLIENT_CERTIFICATE_PATH", ""},
{"AZURE_FEDERATED_TOKEN_FILE", ""},
{"SYSTEM_OIDCREQUESTURI", ""},
{"AZURE_TOKEN_CREDENTIALS",
"prod"}, // <- should result in MIC with useProbeRequest = true
});
return std::make_unique<DefaultAzureCredential>(options);
},
{{"https://azure.com/.default"}},
{{ImATeapot, "{\"expires_in\":3600, \"access_token\":\"ACCESSTOKEN1\"}", {}},
// Given there aren't going to be any retries due to probe request, the credential
// should never get to make a second request to receive the successful response below.
{HttpStatusCode::Ok, "{\"expires_in\":3600, \"access_token\":\"ACCESSTOKEN2\"}", {}}})),
Azure::Core::Credentials::AuthenticationException);
// Everything is the same, including the retry policy, but this time AZURE_TOKEN_CREDENTIALS is
// set to "ManagedIdentityCredential", which should result in ManagedIdentityCredential using
// useProbeRequest = false.
auto const whenProbeDisabled = CredentialTestHelper::SimulateTokenRequest(
[&ImATeapot](auto transport) {
TokenCredentialOptions options;
options.Transport.Transport = transport;
options.Retry.MaxRetries = 3;
options.Retry.RetryDelay = std::chrono::milliseconds(1);
options.Retry.StatusCodes.insert(ImATeapot);
CredentialTestHelper::EnvironmentOverride const env({
// Env vars are set to ensure that all the credential chain is inactive all the way
// up to ManagedIdentityCredential with IMDS source.
{"MSI_ENDPOINT", ""},
{"MSI_SECRET", ""},
{"IDENTITY_ENDPOINT", "https://visualstudio.com/"},
{"IMDS_ENDPOINT", ""},
{"IDENTITY_HEADER", ""},
{"IDENTITY_SERVER_THUMBPRINT", ""},
{"AZURE_POD_IDENTITY_AUTHORITY_HOST", ""},
{"AZURE_AUTHORITY_HOST", ""},
{"AZURE_TENANT_ID", ""},
{"AZURE_CLIENT_ID", ""},
{"AZURE_CLIENT_SECRET", ""},
{"AZURE_CLIENT_CERTIFICATE_PATH", ""},
{"AZURE_FEDERATED_TOKEN_FILE", ""},
{"SYSTEM_OIDCREQUESTURI", ""},
{"AZURE_TOKEN_CREDENTIALS",
"ManagedIdentityCredential"}, // <- should result in MIC with useProbeRequest = false
});
return std::make_unique<DefaultAzureCredential>(options);
},
{{"https://azure.com/.default"}},
{{ImATeapot, "{\"expires_in\":3600, \"access_token\":\"ACCESSTOKEN1\"}", {}},
{HttpStatusCode::Ok, "{\"expires_in\":3600, \"access_token\":\"ACCESSTOKEN2\"}", {}}});
EXPECT_EQ(whenProbeDisabled.Requests.size(), 2U);
EXPECT_EQ(whenProbeDisabled.Responses.size(), 1U);
EXPECT_EQ(whenProbeDisabled.Responses.at(0).AccessToken.Token, "ACCESSTOKEN2");
}

View File

@ -3193,4 +3193,72 @@ namespace Azure { namespace Identity { namespace Test {
Logger::SetListener(nullptr); Logger::SetListener(nullptr);
} }
TEST(ManagedIdentityCredential, ImdsProbe)
{
constexpr auto ImATeapot = static_cast<HttpStatusCode>(418);
EXPECT_THROW(
static_cast<void>(CredentialTestHelper::SimulateTokenRequest(
[&ImATeapot](auto transport) {
ManagedIdentityCredentialOptions options;
options.Transport.Transport = transport;
options.Retry.MaxRetries = 3;
options.Retry.RetryDelay = std::chrono::milliseconds(1);
options.Retry.StatusCodes.insert(ImATeapot);
CredentialTestHelper::EnvironmentOverride const env({
{"MSI_ENDPOINT", ""},
{"MSI_SECRET", ""},
{"IDENTITY_ENDPOINT", "https://visualstudio.com/"},
{"IMDS_ENDPOINT", ""},
{"IDENTITY_HEADER", ""},
{"IDENTITY_SERVER_THUMBPRINT", ""},
{"AZURE_POD_IDENTITY_AUTHORITY_HOST", ""},
});
options.UseProbeRequest = true;
return std::make_unique<ManagedIdentityCredential>(options);
},
{{"https://azure.com/.default"}},
{{ImATeapot, "{\"expires_in\":3600, \"access_token\":\"ACCESSTOKEN1\"}", {}},
// Given there aren't going to be any retries due to probe request, the credential
// should never get to make a second request to receive the successful response below.
{HttpStatusCode::Ok,
"{\"expires_in\":3600, \"access_token\":\"ACCESSTOKEN2\"}",
{}}})),
Azure::Core::Credentials::AuthenticationException);
// Everything is the same, including the retry policy, but this time useProbeRequest = false.
auto const whenProbeDisabled = CredentialTestHelper::SimulateTokenRequest(
[&ImATeapot](auto transport) {
TokenCredentialOptions options;
options.Transport.Transport = transport;
options.Retry.MaxRetries = 3;
options.Retry.RetryDelay = std::chrono::milliseconds(1);
options.Retry.StatusCodes.insert(ImATeapot);
CredentialTestHelper::EnvironmentOverride const env({
{"MSI_ENDPOINT", ""},
{"MSI_SECRET", ""},
{"IDENTITY_ENDPOINT", "https://visualstudio.com/"},
{"IMDS_ENDPOINT", ""},
{"IDENTITY_HEADER", ""},
{"IDENTITY_SERVER_THUMBPRINT", ""},
{"AZURE_POD_IDENTITY_AUTHORITY_HOST", ""},
});
return std::make_unique<ManagedIdentityCredential>(
options); // <-- useProbeRequest = false (default)
},
{{"https://azure.com/.default"}},
{{ImATeapot, "{\"expires_in\":3600, \"access_token\":\"ACCESSTOKEN1\"}", {}},
{HttpStatusCode::Ok, "{\"expires_in\":3600, \"access_token\":\"ACCESSTOKEN2\"}", {}}});
EXPECT_EQ(whenProbeDisabled.Requests.size(), 2U);
EXPECT_EQ(whenProbeDisabled.Responses.size(), 1U);
EXPECT_EQ(whenProbeDisabled.Responses.at(0).AccessToken.Token, "ACCESSTOKEN2");
}
}}} // namespace Azure::Identity::Test }}} // namespace Azure::Identity::Test