Support managed identity auth in AzureFoundry provider - #9707

Merged
Amaury Levé (Evangelink) merged 3 commits into
mainfrom
dev/amauryleve/azurefoundry-managed-identity-auth
Jul 8, 2026
Merged

Support managed identity auth in AzureFoundry provider#9707
Amaury Levé (Evangelink) merged 3 commits into
mainfrom
dev/amauryleve/azurefoundry-managed-identity-auth

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Fixes#8412

Summary

Microsoft.Testing.Extensions.AzureFoundry previously hard-required AZURE_OPENAI_API_KEY and always constructed AzureOpenAIClient with ApiKeyCredential, with no managed-identity / Entra path. This made the only built-in AI provider not secure-by-default for Azure-hosted scenarios.

Changes

  • IsAvailable now only requires AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_DEPLOYMENT_NAME; the API key is no longer mandatory.
  • CreateChatClientAsync prefers an explicit AZURE_OPENAI_API_KEY when present, otherwise falls back to DefaultAzureCredential (managed identity, workload identity, Azure CLI, Visual Studio, …).
  • Added the Azure.Identity package reference (version pinned in Directory.Packages.props).
  • Updated PACKAGE.md with a configuration table documenting both authentication modes.

Notes

  • No .resx/.xlf changes were needed — the existing EnvironmentVariableNotSet string is still used for the endpoint/deployment checks.
  • Built cleanly (netstandard2.0, net8.0, net9.0) with 0 warnings/0 errors.

The AzureFoundry chat client provider now authenticates with
DefaultAzureCredential (Entra ID / managed identity) when no
AZURE_OPENAI_API_KEY is set, keeping the provider secure-by-default
for Azure-hosted scenarios. The API key remains an explicit fallback,
and IsAvailable no longer requires an API key.
Fixes#8412
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings July 7, 2026 14:04

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR makes the Microsoft.Testing.Extensions.AzureFoundry provider support Microsoft Entra ID / managed-identity authentication instead of hard-requiring AZURE_OPENAI_API_KEY, closing the secure-by-default gap described in issue #8412. The provider now reports itself available whenever the endpoint and deployment name are configured, and it authenticates via DefaultAzureCredential when no API key is supplied, keeping the explicit API-key path as an override.

Changes:

  • IsAvailable no longer requires AZURE_OPENAI_API_KEY (only endpoint + deployment name).
  • CreateChatClientAsync uses ApiKeyCredential when a key is present, otherwise falls back to DefaultAzureCredential.
  • Adds the Azure.Identity dependency (version pinned via CPM) and documents both auth modes in PACKAGE.md.
Show a summary per file
FileDescription
src/Platform/Microsoft.Testing.Extensions.AzureFoundry/OpenAIChatClientProvider.csDrops the mandatory API-key check and selects DefaultAzureCredential vs ApiKeyCredential based on whether the key is set.
src/Platform/Microsoft.Testing.Extensions.AzureFoundry/PACKAGE.mdAdds a configuration table and prose documenting the managed-identity and API-key authentication modes.
src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Microsoft.Testing.Extensions.AzureFoundry.csprojReferences the new Azure.Identity package.
Directory.Packages.propsPins Azure.Identity version 1.14.2 under central package management.

Review details

  • Files reviewed: 4/4 changed files
  • Comments generated: 1
  • Review effort level: Medium

Comment threadDirectory.Packages.props Outdated

@github-actionsgithub-actionsBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Note

🤖 Automated review by GitHub Copilot. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.

#DimensionVerdict
13Test Completeness🟡 1 MODERATE
20Build Infrastructure & Dependencies🟡 1 MODERATE
17Documentation Accuracy⚪ 1 NIT

✅ 19/22 dimensions clean (dimensions 10–12, 14, 18–19, 22 skipped as N/A).

  • Test Completeness — no unit tests cover the new DefaultAzureCredential branch (or any path of AzureOpenAIChatClientProvider)
  • Build Infrastructure — Azure.Identity adds a heavyweight transitive dependency graph; acceptable while alpha/non-shipping, but worth revisiting before GA
  • Documentation — consider noting local-dev latency from DefaultAzureCredential credential chain traversal

Overall: The implementation is clean, correct, and well-documented. The ternary conditional for credential selection is straightforward. The IsAvailable change correctly reflects the new optionality of the API key. No correctness, threading, security, or API compatibility concerns.

Comment threadDirectory.Packages.props Outdated
- Reuse a single DefaultAzureCredential instance (Lazy) so the token
cache and credential-chain discovery are shared across calls instead
of being rebuilt on every CreateChatClientAsync invocation.
- Add unit tests for the auth-selection logic (IsAvailable, ModelName,
API-key vs Entra path, missing-variable failures) in
Microsoft.Testing.Extensions.UnitTests, with env-var snapshot/restore
and DoNotParallelize; grant InternalsVisibleTo to the test assembly.
- Document AZURE_CLIENT_ID (user-assigned managed identity) and the
deferred authentication-failure behavior in PACKAGE.md.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment has been minimized.

- Bump Azure.Identity 1.14.2 -> 1.21.0. The 1.14.x line is deprecated
(depends on a deprecated MSAL); 1.21.0 is the current supported
release. Verified clean restore/build across all TFMs (no NU1605,
no audit warnings) and compatible with Azure.AI.OpenAI 2.1.0.
- Extract an internal AuthenticationMode + GetAuthenticationMode seam so
the API-key-vs-DefaultAzureCredential selection is deterministically
unit-tested (upgrades the two 'only IsNotNull' tests and adds three
dedicated selection tests).
- Document DefaultAzureCredential local-dev credential-chain latency in
PACKAGE.md.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review details

  • Files reviewed: 6/6 changed files
  • Comments generated: 0 new
  • Review effort level: Low

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test quality grade — PR #9707

14 new tests graded across 1 file. 12 earn A for clean AAA structure, proper env-var isolation via [DoNotParallelize] + [TestInitialize]/[TestCleanup] snapshot/restore, and precise assertions (equality, boolean state, or exact exception type + message). 2 earn B for mixing two distinct behavioral checks in one test body — extracting the GetAuthenticationMode assertions into their own tests would sharpen those two.

GradeTestNotes
B (80–89)new AzureFoundryChatClientProviderTests.
CreateChatClientAsync_
WithApiKey_
UsesApiKeyPathAndReturnsClient
Mixes a GetAuthenticationMode equality check with IsNotNull on the client — consider separate focused tests per concern.
B (80–89)new AzureFoundryChatClientProviderTests.
CreateChatClientAsync_
WithoutApiKey_
UsesEntraPathAndReturnsClient
Dual-behavior pattern; GetAuthenticationMode(null) is already covered by a dedicated test — consider removing it from this body.
A (90–100)new AzureFoundryChatClientProviderTests.
CreateChatClientAsync_
WhenDeploymentMissing_
Throws
Exact exception type + message content verifies the deployment-missing error path. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
CreateChatClientAsync_
WhenEndpointMissing_
Throws
Exact exception type + message content verifies the endpoint-missing error path. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
GetAuthenticationMode_
WithApiKey_
ReturnsApiKey
Terse expression-bodied; precise equality confirms non-empty key routes to ApiKey mode. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
GetAuthenticationMode_
WithEmptyApiKey_
ReturnsDefaultAzureCredential
Edge case covered; empty string triggers managed identity auth identically to null. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
GetAuthenticationMode_
WithoutApiKey_
ReturnsDefaultAzureCredential
Terse; null maps to DefaultAzureCredential. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
HasToolsCapability_
ReturnsTrue
State assertion on the public contract; constant property — test guards against accidental future changes. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
IsAvailable_
WhenApiKeyAlsoSet_
ReturnsTrue
Confirms availability is unchanged when an API key is also provided. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
IsAvailable_
WhenDeploymentMissing_
ReturnsFalse
Negative test; missing deployment variable prevents availability. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
IsAvailable_
WhenEndpointAndDeploymentSetWithoutApiKey_
ReturnsTrue
Positive test for the new no-API-key availability condition. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
IsAvailable_
WhenEndpointMissing_
ReturnsFalse
Negative test; missing endpoint variable prevents availability. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
ModelName_
ReturnsDeploymentName
Clean equality assertion; deployment variable value is surfaced correctly. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
ModelName_
WhenDeploymentMissing_
ReturnsUnknown
Equality assertion covers the "unknown" fallback when no deployment is configured. No issues found.

This advisory comment was generated automatically. Grades are heuristic
and informational — they do not block merging. Re-run with
/grade-tests.

🤖 Automated content by GitHub Copilot. Generated by the Grade Tests on PR (on open / sync) workflow. · 118.1 AIC · ⌖ 10.5 AIC · ⊞ 9.5K · [◷]( · )

@Evangelink
Amaury Levé (Evangelink) enabled auto-merge (squash) July 8, 2026 07:34
@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jul 8, 2026
@Evangelink
Amaury Levé (Evangelink) merged commit d04f011 into mainJul 8, 2026
63 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/azurefoundry-managed-identity-auth branch July 8, 2026 08:03
github-actionsBot added a commit that referenced this pull request Jul 9, 2026
- AzureFoundry: add DefaultAzureCredential/managed identity auth details
and required environment variables (PR #9707)
- MSTestTestFramework: new entry documenting the native MTP ITestFramework
for MSTest introduced by RFC 018 (PRs #9706, #9743, #9748, #9755)
- VSTestBridge: note MSTest no longer depends on it on the MTP path
as of MSTest 4.3
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Amaury Levé (Evangelink) pushed a commit that referenced this pull request Jul 9, 2026
- AzureFoundry: add DefaultAzureCredential/managed identity auth details
and required environment variables (PR #9707)
- MSTestTestFramework: new entry documenting the native MTP ITestFramework
for MSTest introduced by RFC 018 (PRs #9706, #9743, #9748, #9755)
- VSTestBridge: note MSTest no longer depends on it on the MTP path
as of MSTest 4.3
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state/needs-reviewAwaiting review from the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[MTP Diagnostic Analysis] AzureFoundry hard-requires API keys and has no managed-identity auth path

3 participants

@Evangelink@0101
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Support managed identity auth in AzureFoundry provider - #9707

Merged
Amaury Levé (Evangelink) merged 3 commits into
mainfrom
dev/amauryleve/azurefoundry-managed-identity-auth
Jul 8, 2026
Merged

Support managed identity auth in AzureFoundry provider#9707
Amaury Levé (Evangelink) merged 3 commits into
mainfrom
dev/amauryleve/azurefoundry-managed-identity-auth

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Fixes#8412

Summary

Microsoft.Testing.Extensions.AzureFoundry previously hard-required AZURE_OPENAI_API_KEY and always constructed AzureOpenAIClient with ApiKeyCredential, with no managed-identity / Entra path. This made the only built-in AI provider not secure-by-default for Azure-hosted scenarios.

Changes

  • IsAvailable now only requires AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_DEPLOYMENT_NAME; the API key is no longer mandatory.
  • CreateChatClientAsync prefers an explicit AZURE_OPENAI_API_KEY when present, otherwise falls back to DefaultAzureCredential (managed identity, workload identity, Azure CLI, Visual Studio, …).
  • Added the Azure.Identity package reference (version pinned in Directory.Packages.props).
  • Updated PACKAGE.md with a configuration table documenting both authentication modes.

Notes

  • No .resx/.xlf changes were needed — the existing EnvironmentVariableNotSet string is still used for the endpoint/deployment checks.
  • Built cleanly (netstandard2.0, net8.0, net9.0) with 0 warnings/0 errors.

The AzureFoundry chat client provider now authenticates with
DefaultAzureCredential (Entra ID / managed identity) when no
AZURE_OPENAI_API_KEY is set, keeping the provider secure-by-default
for Azure-hosted scenarios. The API key remains an explicit fallback,
and IsAvailable no longer requires an API key.
Fixes#8412
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings July 7, 2026 14:04

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR makes the Microsoft.Testing.Extensions.AzureFoundry provider support Microsoft Entra ID / managed-identity authentication instead of hard-requiring AZURE_OPENAI_API_KEY, closing the secure-by-default gap described in issue #8412. The provider now reports itself available whenever the endpoint and deployment name are configured, and it authenticates via DefaultAzureCredential when no API key is supplied, keeping the explicit API-key path as an override.

Changes:

  • IsAvailable no longer requires AZURE_OPENAI_API_KEY (only endpoint + deployment name).
  • CreateChatClientAsync uses ApiKeyCredential when a key is present, otherwise falls back to DefaultAzureCredential.
  • Adds the Azure.Identity dependency (version pinned via CPM) and documents both auth modes in PACKAGE.md.
Show a summary per file
FileDescription
src/Platform/Microsoft.Testing.Extensions.AzureFoundry/OpenAIChatClientProvider.csDrops the mandatory API-key check and selects DefaultAzureCredential vs ApiKeyCredential based on whether the key is set.
src/Platform/Microsoft.Testing.Extensions.AzureFoundry/PACKAGE.mdAdds a configuration table and prose documenting the managed-identity and API-key authentication modes.
src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Microsoft.Testing.Extensions.AzureFoundry.csprojReferences the new Azure.Identity package.
Directory.Packages.propsPins Azure.Identity version 1.14.2 under central package management.

Review details

  • Files reviewed: 4/4 changed files
  • Comments generated: 1
  • Review effort level: Medium

Comment threadDirectory.Packages.props Outdated

@github-actionsgithub-actionsBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Note

🤖 Automated review by GitHub Copilot. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.

#DimensionVerdict
13Test Completeness🟡 1 MODERATE
20Build Infrastructure & Dependencies🟡 1 MODERATE
17Documentation Accuracy⚪ 1 NIT

✅ 19/22 dimensions clean (dimensions 10–12, 14, 18–19, 22 skipped as N/A).

  • Test Completeness — no unit tests cover the new DefaultAzureCredential branch (or any path of AzureOpenAIChatClientProvider)
  • Build Infrastructure — Azure.Identity adds a heavyweight transitive dependency graph; acceptable while alpha/non-shipping, but worth revisiting before GA
  • Documentation — consider noting local-dev latency from DefaultAzureCredential credential chain traversal

Overall: The implementation is clean, correct, and well-documented. The ternary conditional for credential selection is straightforward. The IsAvailable change correctly reflects the new optionality of the API key. No correctness, threading, security, or API compatibility concerns.

Comment threadDirectory.Packages.props Outdated
- Reuse a single DefaultAzureCredential instance (Lazy) so the token
cache and credential-chain discovery are shared across calls instead
of being rebuilt on every CreateChatClientAsync invocation.
- Add unit tests for the auth-selection logic (IsAvailable, ModelName,
API-key vs Entra path, missing-variable failures) in
Microsoft.Testing.Extensions.UnitTests, with env-var snapshot/restore
and DoNotParallelize; grant InternalsVisibleTo to the test assembly.
- Document AZURE_CLIENT_ID (user-assigned managed identity) and the
deferred authentication-failure behavior in PACKAGE.md.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment has been minimized.

- Bump Azure.Identity 1.14.2 -> 1.21.0. The 1.14.x line is deprecated
(depends on a deprecated MSAL); 1.21.0 is the current supported
release. Verified clean restore/build across all TFMs (no NU1605,
no audit warnings) and compatible with Azure.AI.OpenAI 2.1.0.
- Extract an internal AuthenticationMode + GetAuthenticationMode seam so
the API-key-vs-DefaultAzureCredential selection is deterministically
unit-tested (upgrades the two 'only IsNotNull' tests and adds three
dedicated selection tests).
- Document DefaultAzureCredential local-dev credential-chain latency in
PACKAGE.md.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review details

  • Files reviewed: 6/6 changed files
  • Comments generated: 0 new
  • Review effort level: Low

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test quality grade — PR #9707

14 new tests graded across 1 file. 12 earn A for clean AAA structure, proper env-var isolation via [DoNotParallelize] + [TestInitialize]/[TestCleanup] snapshot/restore, and precise assertions (equality, boolean state, or exact exception type + message). 2 earn B for mixing two distinct behavioral checks in one test body — extracting the GetAuthenticationMode assertions into their own tests would sharpen those two.

GradeTestNotes
B (80–89)new AzureFoundryChatClientProviderTests.
CreateChatClientAsync_
WithApiKey_
UsesApiKeyPathAndReturnsClient
Mixes a GetAuthenticationMode equality check with IsNotNull on the client — consider separate focused tests per concern.
B (80–89)new AzureFoundryChatClientProviderTests.
CreateChatClientAsync_
WithoutApiKey_
UsesEntraPathAndReturnsClient
Dual-behavior pattern; GetAuthenticationMode(null) is already covered by a dedicated test — consider removing it from this body.
A (90–100)new AzureFoundryChatClientProviderTests.
CreateChatClientAsync_
WhenDeploymentMissing_
Throws
Exact exception type + message content verifies the deployment-missing error path. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
CreateChatClientAsync_
WhenEndpointMissing_
Throws
Exact exception type + message content verifies the endpoint-missing error path. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
GetAuthenticationMode_
WithApiKey_
ReturnsApiKey
Terse expression-bodied; precise equality confirms non-empty key routes to ApiKey mode. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
GetAuthenticationMode_
WithEmptyApiKey_
ReturnsDefaultAzureCredential
Edge case covered; empty string triggers managed identity auth identically to null. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
GetAuthenticationMode_
WithoutApiKey_
ReturnsDefaultAzureCredential
Terse; null maps to DefaultAzureCredential. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
HasToolsCapability_
ReturnsTrue
State assertion on the public contract; constant property — test guards against accidental future changes. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
IsAvailable_
WhenApiKeyAlsoSet_
ReturnsTrue
Confirms availability is unchanged when an API key is also provided. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
IsAvailable_
WhenDeploymentMissing_
ReturnsFalse
Negative test; missing deployment variable prevents availability. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
IsAvailable_
WhenEndpointAndDeploymentSetWithoutApiKey_
ReturnsTrue
Positive test for the new no-API-key availability condition. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
IsAvailable_
WhenEndpointMissing_
ReturnsFalse
Negative test; missing endpoint variable prevents availability. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
ModelName_
ReturnsDeploymentName
Clean equality assertion; deployment variable value is surfaced correctly. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
ModelName_
WhenDeploymentMissing_
ReturnsUnknown
Equality assertion covers the "unknown" fallback when no deployment is configured. No issues found.

This advisory comment was generated automatically. Grades are heuristic
and informational — they do not block merging. Re-run with
/grade-tests.

🤖 Automated content by GitHub Copilot. Generated by the Grade Tests on PR (on open / sync) workflow. · 118.1 AIC · ⌖ 10.5 AIC · ⊞ 9.5K · [◷]( · )

@Evangelink
Amaury Levé (Evangelink) enabled auto-merge (squash) July 8, 2026 07:34
@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jul 8, 2026
@Evangelink
Amaury Levé (Evangelink) merged commit d04f011 into mainJul 8, 2026
63 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/azurefoundry-managed-identity-auth branch July 8, 2026 08:03
github-actionsBot added a commit that referenced this pull request Jul 9, 2026
- AzureFoundry: add DefaultAzureCredential/managed identity auth details
and required environment variables (PR #9707)
- MSTestTestFramework: new entry documenting the native MTP ITestFramework
for MSTest introduced by RFC 018 (PRs #9706, #9743, #9748, #9755)
- VSTestBridge: note MSTest no longer depends on it on the MTP path
as of MSTest 4.3
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Amaury Levé (Evangelink) pushed a commit that referenced this pull request Jul 9, 2026
- AzureFoundry: add DefaultAzureCredential/managed identity auth details
and required environment variables (PR #9707)
- MSTestTestFramework: new entry documenting the native MTP ITestFramework
for MSTest introduced by RFC 018 (PRs #9706, #9743, #9748, #9755)
- VSTestBridge: note MSTest no longer depends on it on the MTP path
as of MSTest 4.3
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state/needs-reviewAwaiting review from the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[MTP Diagnostic Analysis] AzureFoundry hard-requires API keys and has no managed-identity auth path

3 participants

@Evangelink@0101
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Support managed identity auth in AzureFoundry provider - #9707

Merged
Amaury Levé (Evangelink) merged 3 commits into
mainfrom
dev/amauryleve/azurefoundry-managed-identity-auth
Jul 8, 2026
Merged

Support managed identity auth in AzureFoundry provider#9707
Amaury Levé (Evangelink) merged 3 commits into
mainfrom
dev/amauryleve/azurefoundry-managed-identity-auth

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Fixes#8412

Summary

Microsoft.Testing.Extensions.AzureFoundry previously hard-required AZURE_OPENAI_API_KEY and always constructed AzureOpenAIClient with ApiKeyCredential, with no managed-identity / Entra path. This made the only built-in AI provider not secure-by-default for Azure-hosted scenarios.

Changes

  • IsAvailable now only requires AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_DEPLOYMENT_NAME; the API key is no longer mandatory.
  • CreateChatClientAsync prefers an explicit AZURE_OPENAI_API_KEY when present, otherwise falls back to DefaultAzureCredential (managed identity, workload identity, Azure CLI, Visual Studio, …).
  • Added the Azure.Identity package reference (version pinned in Directory.Packages.props).
  • Updated PACKAGE.md with a configuration table documenting both authentication modes.

Notes

  • No .resx/.xlf changes were needed — the existing EnvironmentVariableNotSet string is still used for the endpoint/deployment checks.
  • Built cleanly (netstandard2.0, net8.0, net9.0) with 0 warnings/0 errors.

The AzureFoundry chat client provider now authenticates with
DefaultAzureCredential (Entra ID / managed identity) when no
AZURE_OPENAI_API_KEY is set, keeping the provider secure-by-default
for Azure-hosted scenarios. The API key remains an explicit fallback,
and IsAvailable no longer requires an API key.
Fixes#8412
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings July 7, 2026 14:04

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR makes the Microsoft.Testing.Extensions.AzureFoundry provider support Microsoft Entra ID / managed-identity authentication instead of hard-requiring AZURE_OPENAI_API_KEY, closing the secure-by-default gap described in issue #8412. The provider now reports itself available whenever the endpoint and deployment name are configured, and it authenticates via DefaultAzureCredential when no API key is supplied, keeping the explicit API-key path as an override.

Changes:

  • IsAvailable no longer requires AZURE_OPENAI_API_KEY (only endpoint + deployment name).
  • CreateChatClientAsync uses ApiKeyCredential when a key is present, otherwise falls back to DefaultAzureCredential.
  • Adds the Azure.Identity dependency (version pinned via CPM) and documents both auth modes in PACKAGE.md.
Show a summary per file
FileDescription
src/Platform/Microsoft.Testing.Extensions.AzureFoundry/OpenAIChatClientProvider.csDrops the mandatory API-key check and selects DefaultAzureCredential vs ApiKeyCredential based on whether the key is set.
src/Platform/Microsoft.Testing.Extensions.AzureFoundry/PACKAGE.mdAdds a configuration table and prose documenting the managed-identity and API-key authentication modes.
src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Microsoft.Testing.Extensions.AzureFoundry.csprojReferences the new Azure.Identity package.
Directory.Packages.propsPins Azure.Identity version 1.14.2 under central package management.

Review details

  • Files reviewed: 4/4 changed files
  • Comments generated: 1
  • Review effort level: Medium

Comment threadDirectory.Packages.props Outdated

@github-actionsgithub-actionsBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Note

🤖 Automated review by GitHub Copilot. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.

#DimensionVerdict
13Test Completeness🟡 1 MODERATE
20Build Infrastructure & Dependencies🟡 1 MODERATE
17Documentation Accuracy⚪ 1 NIT

✅ 19/22 dimensions clean (dimensions 10–12, 14, 18–19, 22 skipped as N/A).

  • Test Completeness — no unit tests cover the new DefaultAzureCredential branch (or any path of AzureOpenAIChatClientProvider)
  • Build Infrastructure — Azure.Identity adds a heavyweight transitive dependency graph; acceptable while alpha/non-shipping, but worth revisiting before GA
  • Documentation — consider noting local-dev latency from DefaultAzureCredential credential chain traversal

Overall: The implementation is clean, correct, and well-documented. The ternary conditional for credential selection is straightforward. The IsAvailable change correctly reflects the new optionality of the API key. No correctness, threading, security, or API compatibility concerns.

Comment threadDirectory.Packages.props Outdated
- Reuse a single DefaultAzureCredential instance (Lazy) so the token
cache and credential-chain discovery are shared across calls instead
of being rebuilt on every CreateChatClientAsync invocation.
- Add unit tests for the auth-selection logic (IsAvailable, ModelName,
API-key vs Entra path, missing-variable failures) in
Microsoft.Testing.Extensions.UnitTests, with env-var snapshot/restore
and DoNotParallelize; grant InternalsVisibleTo to the test assembly.
- Document AZURE_CLIENT_ID (user-assigned managed identity) and the
deferred authentication-failure behavior in PACKAGE.md.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment has been minimized.

- Bump Azure.Identity 1.14.2 -> 1.21.0. The 1.14.x line is deprecated
(depends on a deprecated MSAL); 1.21.0 is the current supported
release. Verified clean restore/build across all TFMs (no NU1605,
no audit warnings) and compatible with Azure.AI.OpenAI 2.1.0.
- Extract an internal AuthenticationMode + GetAuthenticationMode seam so
the API-key-vs-DefaultAzureCredential selection is deterministically
unit-tested (upgrades the two 'only IsNotNull' tests and adds three
dedicated selection tests).
- Document DefaultAzureCredential local-dev credential-chain latency in
PACKAGE.md.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review details

  • Files reviewed: 6/6 changed files
  • Comments generated: 0 new
  • Review effort level: Low

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test quality grade — PR #9707

14 new tests graded across 1 file. 12 earn A for clean AAA structure, proper env-var isolation via [DoNotParallelize] + [TestInitialize]/[TestCleanup] snapshot/restore, and precise assertions (equality, boolean state, or exact exception type + message). 2 earn B for mixing two distinct behavioral checks in one test body — extracting the GetAuthenticationMode assertions into their own tests would sharpen those two.

GradeTestNotes
B (80–89)new AzureFoundryChatClientProviderTests.
CreateChatClientAsync_
WithApiKey_
UsesApiKeyPathAndReturnsClient
Mixes a GetAuthenticationMode equality check with IsNotNull on the client — consider separate focused tests per concern.
B (80–89)new AzureFoundryChatClientProviderTests.
CreateChatClientAsync_
WithoutApiKey_
UsesEntraPathAndReturnsClient
Dual-behavior pattern; GetAuthenticationMode(null) is already covered by a dedicated test — consider removing it from this body.
A (90–100)new AzureFoundryChatClientProviderTests.
CreateChatClientAsync_
WhenDeploymentMissing_
Throws
Exact exception type + message content verifies the deployment-missing error path. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
CreateChatClientAsync_
WhenEndpointMissing_
Throws
Exact exception type + message content verifies the endpoint-missing error path. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
GetAuthenticationMode_
WithApiKey_
ReturnsApiKey
Terse expression-bodied; precise equality confirms non-empty key routes to ApiKey mode. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
GetAuthenticationMode_
WithEmptyApiKey_
ReturnsDefaultAzureCredential
Edge case covered; empty string triggers managed identity auth identically to null. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
GetAuthenticationMode_
WithoutApiKey_
ReturnsDefaultAzureCredential
Terse; null maps to DefaultAzureCredential. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
HasToolsCapability_
ReturnsTrue
State assertion on the public contract; constant property — test guards against accidental future changes. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
IsAvailable_
WhenApiKeyAlsoSet_
ReturnsTrue
Confirms availability is unchanged when an API key is also provided. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
IsAvailable_
WhenDeploymentMissing_
ReturnsFalse
Negative test; missing deployment variable prevents availability. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
IsAvailable_
WhenEndpointAndDeploymentSetWithoutApiKey_
ReturnsTrue
Positive test for the new no-API-key availability condition. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
IsAvailable_
WhenEndpointMissing_
ReturnsFalse
Negative test; missing endpoint variable prevents availability. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
ModelName_
ReturnsDeploymentName
Clean equality assertion; deployment variable value is surfaced correctly. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
ModelName_
WhenDeploymentMissing_
ReturnsUnknown
Equality assertion covers the "unknown" fallback when no deployment is configured. No issues found.

This advisory comment was generated automatically. Grades are heuristic
and informational — they do not block merging. Re-run with
/grade-tests.

🤖 Automated content by GitHub Copilot. Generated by the Grade Tests on PR (on open / sync) workflow. · 118.1 AIC · ⌖ 10.5 AIC · ⊞ 9.5K · [◷]( · )

@Evangelink
Amaury Levé (Evangelink) enabled auto-merge (squash) July 8, 2026 07:34
@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jul 8, 2026
@Evangelink
Amaury Levé (Evangelink) merged commit d04f011 into mainJul 8, 2026
63 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/azurefoundry-managed-identity-auth branch July 8, 2026 08:03
github-actionsBot added a commit that referenced this pull request Jul 9, 2026
- AzureFoundry: add DefaultAzureCredential/managed identity auth details
and required environment variables (PR #9707)
- MSTestTestFramework: new entry documenting the native MTP ITestFramework
for MSTest introduced by RFC 018 (PRs #9706, #9743, #9748, #9755)
- VSTestBridge: note MSTest no longer depends on it on the MTP path
as of MSTest 4.3
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Amaury Levé (Evangelink) pushed a commit that referenced this pull request Jul 9, 2026
- AzureFoundry: add DefaultAzureCredential/managed identity auth details
and required environment variables (PR #9707)
- MSTestTestFramework: new entry documenting the native MTP ITestFramework
for MSTest introduced by RFC 018 (PRs #9706, #9743, #9748, #9755)
- VSTestBridge: note MSTest no longer depends on it on the MTP path
as of MSTest 4.3
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state/needs-reviewAwaiting review from the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[MTP Diagnostic Analysis] AzureFoundry hard-requires API keys and has no managed-identity auth path

3 participants

@Evangelink@0101
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Support managed identity auth in AzureFoundry provider - #9707

Merged
Amaury Levé (Evangelink) merged 3 commits into
mainfrom
dev/amauryleve/azurefoundry-managed-identity-auth
Jul 8, 2026
Merged

Support managed identity auth in AzureFoundry provider#9707
Amaury Levé (Evangelink) merged 3 commits into
mainfrom
dev/amauryleve/azurefoundry-managed-identity-auth

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Fixes#8412

Summary

Microsoft.Testing.Extensions.AzureFoundry previously hard-required AZURE_OPENAI_API_KEY and always constructed AzureOpenAIClient with ApiKeyCredential, with no managed-identity / Entra path. This made the only built-in AI provider not secure-by-default for Azure-hosted scenarios.

Changes

  • IsAvailable now only requires AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_DEPLOYMENT_NAME; the API key is no longer mandatory.
  • CreateChatClientAsync prefers an explicit AZURE_OPENAI_API_KEY when present, otherwise falls back to DefaultAzureCredential (managed identity, workload identity, Azure CLI, Visual Studio, …).
  • Added the Azure.Identity package reference (version pinned in Directory.Packages.props).
  • Updated PACKAGE.md with a configuration table documenting both authentication modes.

Notes

  • No .resx/.xlf changes were needed — the existing EnvironmentVariableNotSet string is still used for the endpoint/deployment checks.
  • Built cleanly (netstandard2.0, net8.0, net9.0) with 0 warnings/0 errors.

The AzureFoundry chat client provider now authenticates with
DefaultAzureCredential (Entra ID / managed identity) when no
AZURE_OPENAI_API_KEY is set, keeping the provider secure-by-default
for Azure-hosted scenarios. The API key remains an explicit fallback,
and IsAvailable no longer requires an API key.
Fixes#8412
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings July 7, 2026 14:04

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR makes the Microsoft.Testing.Extensions.AzureFoundry provider support Microsoft Entra ID / managed-identity authentication instead of hard-requiring AZURE_OPENAI_API_KEY, closing the secure-by-default gap described in issue #8412. The provider now reports itself available whenever the endpoint and deployment name are configured, and it authenticates via DefaultAzureCredential when no API key is supplied, keeping the explicit API-key path as an override.

Changes:

  • IsAvailable no longer requires AZURE_OPENAI_API_KEY (only endpoint + deployment name).
  • CreateChatClientAsync uses ApiKeyCredential when a key is present, otherwise falls back to DefaultAzureCredential.
  • Adds the Azure.Identity dependency (version pinned via CPM) and documents both auth modes in PACKAGE.md.
Show a summary per file
FileDescription
src/Platform/Microsoft.Testing.Extensions.AzureFoundry/OpenAIChatClientProvider.csDrops the mandatory API-key check and selects DefaultAzureCredential vs ApiKeyCredential based on whether the key is set.
src/Platform/Microsoft.Testing.Extensions.AzureFoundry/PACKAGE.mdAdds a configuration table and prose documenting the managed-identity and API-key authentication modes.
src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Microsoft.Testing.Extensions.AzureFoundry.csprojReferences the new Azure.Identity package.
Directory.Packages.propsPins Azure.Identity version 1.14.2 under central package management.

Review details

  • Files reviewed: 4/4 changed files
  • Comments generated: 1
  • Review effort level: Medium

Comment threadDirectory.Packages.props Outdated

@github-actionsgithub-actionsBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Note

🤖 Automated review by GitHub Copilot. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.

#DimensionVerdict
13Test Completeness🟡 1 MODERATE
20Build Infrastructure & Dependencies🟡 1 MODERATE
17Documentation Accuracy⚪ 1 NIT

✅ 19/22 dimensions clean (dimensions 10–12, 14, 18–19, 22 skipped as N/A).

  • Test Completeness — no unit tests cover the new DefaultAzureCredential branch (or any path of AzureOpenAIChatClientProvider)
  • Build Infrastructure — Azure.Identity adds a heavyweight transitive dependency graph; acceptable while alpha/non-shipping, but worth revisiting before GA
  • Documentation — consider noting local-dev latency from DefaultAzureCredential credential chain traversal

Overall: The implementation is clean, correct, and well-documented. The ternary conditional for credential selection is straightforward. The IsAvailable change correctly reflects the new optionality of the API key. No correctness, threading, security, or API compatibility concerns.

Comment threadDirectory.Packages.props Outdated
- Reuse a single DefaultAzureCredential instance (Lazy) so the token
cache and credential-chain discovery are shared across calls instead
of being rebuilt on every CreateChatClientAsync invocation.
- Add unit tests for the auth-selection logic (IsAvailable, ModelName,
API-key vs Entra path, missing-variable failures) in
Microsoft.Testing.Extensions.UnitTests, with env-var snapshot/restore
and DoNotParallelize; grant InternalsVisibleTo to the test assembly.
- Document AZURE_CLIENT_ID (user-assigned managed identity) and the
deferred authentication-failure behavior in PACKAGE.md.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment has been minimized.

- Bump Azure.Identity 1.14.2 -> 1.21.0. The 1.14.x line is deprecated
(depends on a deprecated MSAL); 1.21.0 is the current supported
release. Verified clean restore/build across all TFMs (no NU1605,
no audit warnings) and compatible with Azure.AI.OpenAI 2.1.0.
- Extract an internal AuthenticationMode + GetAuthenticationMode seam so
the API-key-vs-DefaultAzureCredential selection is deterministically
unit-tested (upgrades the two 'only IsNotNull' tests and adds three
dedicated selection tests).
- Document DefaultAzureCredential local-dev credential-chain latency in
PACKAGE.md.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review details

  • Files reviewed: 6/6 changed files
  • Comments generated: 0 new
  • Review effort level: Low

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test quality grade — PR #9707

14 new tests graded across 1 file. 12 earn A for clean AAA structure, proper env-var isolation via [DoNotParallelize] + [TestInitialize]/[TestCleanup] snapshot/restore, and precise assertions (equality, boolean state, or exact exception type + message). 2 earn B for mixing two distinct behavioral checks in one test body — extracting the GetAuthenticationMode assertions into their own tests would sharpen those two.

GradeTestNotes
B (80–89)new AzureFoundryChatClientProviderTests.
CreateChatClientAsync_
WithApiKey_
UsesApiKeyPathAndReturnsClient
Mixes a GetAuthenticationMode equality check with IsNotNull on the client — consider separate focused tests per concern.
B (80–89)new AzureFoundryChatClientProviderTests.
CreateChatClientAsync_
WithoutApiKey_
UsesEntraPathAndReturnsClient
Dual-behavior pattern; GetAuthenticationMode(null) is already covered by a dedicated test — consider removing it from this body.
A (90–100)new AzureFoundryChatClientProviderTests.
CreateChatClientAsync_
WhenDeploymentMissing_
Throws
Exact exception type + message content verifies the deployment-missing error path. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
CreateChatClientAsync_
WhenEndpointMissing_
Throws
Exact exception type + message content verifies the endpoint-missing error path. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
GetAuthenticationMode_
WithApiKey_
ReturnsApiKey
Terse expression-bodied; precise equality confirms non-empty key routes to ApiKey mode. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
GetAuthenticationMode_
WithEmptyApiKey_
ReturnsDefaultAzureCredential
Edge case covered; empty string triggers managed identity auth identically to null. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
GetAuthenticationMode_
WithoutApiKey_
ReturnsDefaultAzureCredential
Terse; null maps to DefaultAzureCredential. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
HasToolsCapability_
ReturnsTrue
State assertion on the public contract; constant property — test guards against accidental future changes. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
IsAvailable_
WhenApiKeyAlsoSet_
ReturnsTrue
Confirms availability is unchanged when an API key is also provided. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
IsAvailable_
WhenDeploymentMissing_
ReturnsFalse
Negative test; missing deployment variable prevents availability. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
IsAvailable_
WhenEndpointAndDeploymentSetWithoutApiKey_
ReturnsTrue
Positive test for the new no-API-key availability condition. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
IsAvailable_
WhenEndpointMissing_
ReturnsFalse
Negative test; missing endpoint variable prevents availability. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
ModelName_
ReturnsDeploymentName
Clean equality assertion; deployment variable value is surfaced correctly. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
ModelName_
WhenDeploymentMissing_
ReturnsUnknown
Equality assertion covers the "unknown" fallback when no deployment is configured. No issues found.

This advisory comment was generated automatically. Grades are heuristic
and informational — they do not block merging. Re-run with
/grade-tests.

🤖 Automated content by GitHub Copilot. Generated by the Grade Tests on PR (on open / sync) workflow. · 118.1 AIC · ⌖ 10.5 AIC · ⊞ 9.5K · [◷]( · )

@Evangelink
Amaury Levé (Evangelink) enabled auto-merge (squash) July 8, 2026 07:34
@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jul 8, 2026
@Evangelink
Amaury Levé (Evangelink) merged commit d04f011 into mainJul 8, 2026
63 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/azurefoundry-managed-identity-auth branch July 8, 2026 08:03
github-actionsBot added a commit that referenced this pull request Jul 9, 2026
- AzureFoundry: add DefaultAzureCredential/managed identity auth details
and required environment variables (PR #9707)
- MSTestTestFramework: new entry documenting the native MTP ITestFramework
for MSTest introduced by RFC 018 (PRs #9706, #9743, #9748, #9755)
- VSTestBridge: note MSTest no longer depends on it on the MTP path
as of MSTest 4.3
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Amaury Levé (Evangelink) pushed a commit that referenced this pull request Jul 9, 2026
- AzureFoundry: add DefaultAzureCredential/managed identity auth details
and required environment variables (PR #9707)
- MSTestTestFramework: new entry documenting the native MTP ITestFramework
for MSTest introduced by RFC 018 (PRs #9706, #9743, #9748, #9755)
- VSTestBridge: note MSTest no longer depends on it on the MTP path
as of MSTest 4.3
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state/needs-reviewAwaiting review from the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[MTP Diagnostic Analysis] AzureFoundry hard-requires API keys and has no managed-identity auth path

3 participants

@Evangelink@0101
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Support managed identity auth in AzureFoundry provider - #9707

Merged
Amaury Levé (Evangelink) merged 3 commits into
mainfrom
dev/amauryleve/azurefoundry-managed-identity-auth
Jul 8, 2026
Merged

Support managed identity auth in AzureFoundry provider#9707
Amaury Levé (Evangelink) merged 3 commits into
mainfrom
dev/amauryleve/azurefoundry-managed-identity-auth

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Fixes#8412

Summary

Microsoft.Testing.Extensions.AzureFoundry previously hard-required AZURE_OPENAI_API_KEY and always constructed AzureOpenAIClient with ApiKeyCredential, with no managed-identity / Entra path. This made the only built-in AI provider not secure-by-default for Azure-hosted scenarios.

Changes

  • IsAvailable now only requires AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_DEPLOYMENT_NAME; the API key is no longer mandatory.
  • CreateChatClientAsync prefers an explicit AZURE_OPENAI_API_KEY when present, otherwise falls back to DefaultAzureCredential (managed identity, workload identity, Azure CLI, Visual Studio, …).
  • Added the Azure.Identity package reference (version pinned in Directory.Packages.props).
  • Updated PACKAGE.md with a configuration table documenting both authentication modes.

Notes

  • No .resx/.xlf changes were needed — the existing EnvironmentVariableNotSet string is still used for the endpoint/deployment checks.
  • Built cleanly (netstandard2.0, net8.0, net9.0) with 0 warnings/0 errors.

The AzureFoundry chat client provider now authenticates with
DefaultAzureCredential (Entra ID / managed identity) when no
AZURE_OPENAI_API_KEY is set, keeping the provider secure-by-default
for Azure-hosted scenarios. The API key remains an explicit fallback,
and IsAvailable no longer requires an API key.
Fixes#8412
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings July 7, 2026 14:04

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR makes the Microsoft.Testing.Extensions.AzureFoundry provider support Microsoft Entra ID / managed-identity authentication instead of hard-requiring AZURE_OPENAI_API_KEY, closing the secure-by-default gap described in issue #8412. The provider now reports itself available whenever the endpoint and deployment name are configured, and it authenticates via DefaultAzureCredential when no API key is supplied, keeping the explicit API-key path as an override.

Changes:

  • IsAvailable no longer requires AZURE_OPENAI_API_KEY (only endpoint + deployment name).
  • CreateChatClientAsync uses ApiKeyCredential when a key is present, otherwise falls back to DefaultAzureCredential.
  • Adds the Azure.Identity dependency (version pinned via CPM) and documents both auth modes in PACKAGE.md.
Show a summary per file
FileDescription
src/Platform/Microsoft.Testing.Extensions.AzureFoundry/OpenAIChatClientProvider.csDrops the mandatory API-key check and selects DefaultAzureCredential vs ApiKeyCredential based on whether the key is set.
src/Platform/Microsoft.Testing.Extensions.AzureFoundry/PACKAGE.mdAdds a configuration table and prose documenting the managed-identity and API-key authentication modes.
src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Microsoft.Testing.Extensions.AzureFoundry.csprojReferences the new Azure.Identity package.
Directory.Packages.propsPins Azure.Identity version 1.14.2 under central package management.

Review details

  • Files reviewed: 4/4 changed files
  • Comments generated: 1
  • Review effort level: Medium

Comment threadDirectory.Packages.props Outdated

@github-actionsgithub-actionsBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Note

🤖 Automated review by GitHub Copilot. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.

#DimensionVerdict
13Test Completeness🟡 1 MODERATE
20Build Infrastructure & Dependencies🟡 1 MODERATE
17Documentation Accuracy⚪ 1 NIT

✅ 19/22 dimensions clean (dimensions 10–12, 14, 18–19, 22 skipped as N/A).

  • Test Completeness — no unit tests cover the new DefaultAzureCredential branch (or any path of AzureOpenAIChatClientProvider)
  • Build Infrastructure — Azure.Identity adds a heavyweight transitive dependency graph; acceptable while alpha/non-shipping, but worth revisiting before GA
  • Documentation — consider noting local-dev latency from DefaultAzureCredential credential chain traversal

Overall: The implementation is clean, correct, and well-documented. The ternary conditional for credential selection is straightforward. The IsAvailable change correctly reflects the new optionality of the API key. No correctness, threading, security, or API compatibility concerns.

Comment threadDirectory.Packages.props Outdated
- Reuse a single DefaultAzureCredential instance (Lazy) so the token
cache and credential-chain discovery are shared across calls instead
of being rebuilt on every CreateChatClientAsync invocation.
- Add unit tests for the auth-selection logic (IsAvailable, ModelName,
API-key vs Entra path, missing-variable failures) in
Microsoft.Testing.Extensions.UnitTests, with env-var snapshot/restore
and DoNotParallelize; grant InternalsVisibleTo to the test assembly.
- Document AZURE_CLIENT_ID (user-assigned managed identity) and the
deferred authentication-failure behavior in PACKAGE.md.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment has been minimized.

- Bump Azure.Identity 1.14.2 -> 1.21.0. The 1.14.x line is deprecated
(depends on a deprecated MSAL); 1.21.0 is the current supported
release. Verified clean restore/build across all TFMs (no NU1605,
no audit warnings) and compatible with Azure.AI.OpenAI 2.1.0.
- Extract an internal AuthenticationMode + GetAuthenticationMode seam so
the API-key-vs-DefaultAzureCredential selection is deterministically
unit-tested (upgrades the two 'only IsNotNull' tests and adds three
dedicated selection tests).
- Document DefaultAzureCredential local-dev credential-chain latency in
PACKAGE.md.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review details

  • Files reviewed: 6/6 changed files
  • Comments generated: 0 new
  • Review effort level: Low

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test quality grade — PR #9707

14 new tests graded across 1 file. 12 earn A for clean AAA structure, proper env-var isolation via [DoNotParallelize] + [TestInitialize]/[TestCleanup] snapshot/restore, and precise assertions (equality, boolean state, or exact exception type + message). 2 earn B for mixing two distinct behavioral checks in one test body — extracting the GetAuthenticationMode assertions into their own tests would sharpen those two.

GradeTestNotes
B (80–89)new AzureFoundryChatClientProviderTests.
CreateChatClientAsync_
WithApiKey_
UsesApiKeyPathAndReturnsClient
Mixes a GetAuthenticationMode equality check with IsNotNull on the client — consider separate focused tests per concern.
B (80–89)new AzureFoundryChatClientProviderTests.
CreateChatClientAsync_
WithoutApiKey_
UsesEntraPathAndReturnsClient
Dual-behavior pattern; GetAuthenticationMode(null) is already covered by a dedicated test — consider removing it from this body.
A (90–100)new AzureFoundryChatClientProviderTests.
CreateChatClientAsync_
WhenDeploymentMissing_
Throws
Exact exception type + message content verifies the deployment-missing error path. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
CreateChatClientAsync_
WhenEndpointMissing_
Throws
Exact exception type + message content verifies the endpoint-missing error path. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
GetAuthenticationMode_
WithApiKey_
ReturnsApiKey
Terse expression-bodied; precise equality confirms non-empty key routes to ApiKey mode. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
GetAuthenticationMode_
WithEmptyApiKey_
ReturnsDefaultAzureCredential
Edge case covered; empty string triggers managed identity auth identically to null. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
GetAuthenticationMode_
WithoutApiKey_
ReturnsDefaultAzureCredential
Terse; null maps to DefaultAzureCredential. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
HasToolsCapability_
ReturnsTrue
State assertion on the public contract; constant property — test guards against accidental future changes. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
IsAvailable_
WhenApiKeyAlsoSet_
ReturnsTrue
Confirms availability is unchanged when an API key is also provided. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
IsAvailable_
WhenDeploymentMissing_
ReturnsFalse
Negative test; missing deployment variable prevents availability. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
IsAvailable_
WhenEndpointAndDeploymentSetWithoutApiKey_
ReturnsTrue
Positive test for the new no-API-key availability condition. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
IsAvailable_
WhenEndpointMissing_
ReturnsFalse
Negative test; missing endpoint variable prevents availability. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
ModelName_
ReturnsDeploymentName
Clean equality assertion; deployment variable value is surfaced correctly. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
ModelName_
WhenDeploymentMissing_
ReturnsUnknown
Equality assertion covers the "unknown" fallback when no deployment is configured. No issues found.

This advisory comment was generated automatically. Grades are heuristic
and informational — they do not block merging. Re-run with
/grade-tests.

🤖 Automated content by GitHub Copilot. Generated by the Grade Tests on PR (on open / sync) workflow. · 118.1 AIC · ⌖ 10.5 AIC · ⊞ 9.5K · [◷]( · )

@Evangelink
Amaury Levé (Evangelink) enabled auto-merge (squash) July 8, 2026 07:34
@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jul 8, 2026
@Evangelink
Amaury Levé (Evangelink) merged commit d04f011 into mainJul 8, 2026
63 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/azurefoundry-managed-identity-auth branch July 8, 2026 08:03
github-actionsBot added a commit that referenced this pull request Jul 9, 2026
- AzureFoundry: add DefaultAzureCredential/managed identity auth details
and required environment variables (PR #9707)
- MSTestTestFramework: new entry documenting the native MTP ITestFramework
for MSTest introduced by RFC 018 (PRs #9706, #9743, #9748, #9755)
- VSTestBridge: note MSTest no longer depends on it on the MTP path
as of MSTest 4.3
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Amaury Levé (Evangelink) pushed a commit that referenced this pull request Jul 9, 2026
- AzureFoundry: add DefaultAzureCredential/managed identity auth details
and required environment variables (PR #9707)
- MSTestTestFramework: new entry documenting the native MTP ITestFramework
for MSTest introduced by RFC 018 (PRs #9706, #9743, #9748, #9755)
- VSTestBridge: note MSTest no longer depends on it on the MTP path
as of MSTest 4.3
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state/needs-reviewAwaiting review from the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[MTP Diagnostic Analysis] AzureFoundry hard-requires API keys and has no managed-identity auth path

3 participants

@Evangelink@0101
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Support managed identity auth in AzureFoundry provider - #9707

Merged
Amaury Levé (Evangelink) merged 3 commits into
mainfrom
dev/amauryleve/azurefoundry-managed-identity-auth
Jul 8, 2026
Merged

Support managed identity auth in AzureFoundry provider#9707
Amaury Levé (Evangelink) merged 3 commits into
mainfrom
dev/amauryleve/azurefoundry-managed-identity-auth

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Fixes#8412

Summary

Microsoft.Testing.Extensions.AzureFoundry previously hard-required AZURE_OPENAI_API_KEY and always constructed AzureOpenAIClient with ApiKeyCredential, with no managed-identity / Entra path. This made the only built-in AI provider not secure-by-default for Azure-hosted scenarios.

Changes

  • IsAvailable now only requires AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_DEPLOYMENT_NAME; the API key is no longer mandatory.
  • CreateChatClientAsync prefers an explicit AZURE_OPENAI_API_KEY when present, otherwise falls back to DefaultAzureCredential (managed identity, workload identity, Azure CLI, Visual Studio, …).
  • Added the Azure.Identity package reference (version pinned in Directory.Packages.props).
  • Updated PACKAGE.md with a configuration table documenting both authentication modes.

Notes

  • No .resx/.xlf changes were needed — the existing EnvironmentVariableNotSet string is still used for the endpoint/deployment checks.
  • Built cleanly (netstandard2.0, net8.0, net9.0) with 0 warnings/0 errors.

The AzureFoundry chat client provider now authenticates with
DefaultAzureCredential (Entra ID / managed identity) when no
AZURE_OPENAI_API_KEY is set, keeping the provider secure-by-default
for Azure-hosted scenarios. The API key remains an explicit fallback,
and IsAvailable no longer requires an API key.
Fixes#8412
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings July 7, 2026 14:04

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR makes the Microsoft.Testing.Extensions.AzureFoundry provider support Microsoft Entra ID / managed-identity authentication instead of hard-requiring AZURE_OPENAI_API_KEY, closing the secure-by-default gap described in issue #8412. The provider now reports itself available whenever the endpoint and deployment name are configured, and it authenticates via DefaultAzureCredential when no API key is supplied, keeping the explicit API-key path as an override.

Changes:

  • IsAvailable no longer requires AZURE_OPENAI_API_KEY (only endpoint + deployment name).
  • CreateChatClientAsync uses ApiKeyCredential when a key is present, otherwise falls back to DefaultAzureCredential.
  • Adds the Azure.Identity dependency (version pinned via CPM) and documents both auth modes in PACKAGE.md.
Show a summary per file
FileDescription
src/Platform/Microsoft.Testing.Extensions.AzureFoundry/OpenAIChatClientProvider.csDrops the mandatory API-key check and selects DefaultAzureCredential vs ApiKeyCredential based on whether the key is set.
src/Platform/Microsoft.Testing.Extensions.AzureFoundry/PACKAGE.mdAdds a configuration table and prose documenting the managed-identity and API-key authentication modes.
src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Microsoft.Testing.Extensions.AzureFoundry.csprojReferences the new Azure.Identity package.
Directory.Packages.propsPins Azure.Identity version 1.14.2 under central package management.

Review details

  • Files reviewed: 4/4 changed files
  • Comments generated: 1
  • Review effort level: Medium

Comment threadDirectory.Packages.props Outdated

@github-actionsgithub-actionsBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Note

🤖 Automated review by GitHub Copilot. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.

#DimensionVerdict
13Test Completeness🟡 1 MODERATE
20Build Infrastructure & Dependencies🟡 1 MODERATE
17Documentation Accuracy⚪ 1 NIT

✅ 19/22 dimensions clean (dimensions 10–12, 14, 18–19, 22 skipped as N/A).

  • Test Completeness — no unit tests cover the new DefaultAzureCredential branch (or any path of AzureOpenAIChatClientProvider)
  • Build Infrastructure — Azure.Identity adds a heavyweight transitive dependency graph; acceptable while alpha/non-shipping, but worth revisiting before GA
  • Documentation — consider noting local-dev latency from DefaultAzureCredential credential chain traversal

Overall: The implementation is clean, correct, and well-documented. The ternary conditional for credential selection is straightforward. The IsAvailable change correctly reflects the new optionality of the API key. No correctness, threading, security, or API compatibility concerns.

Comment threadDirectory.Packages.props Outdated
- Reuse a single DefaultAzureCredential instance (Lazy) so the token
cache and credential-chain discovery are shared across calls instead
of being rebuilt on every CreateChatClientAsync invocation.
- Add unit tests for the auth-selection logic (IsAvailable, ModelName,
API-key vs Entra path, missing-variable failures) in
Microsoft.Testing.Extensions.UnitTests, with env-var snapshot/restore
and DoNotParallelize; grant InternalsVisibleTo to the test assembly.
- Document AZURE_CLIENT_ID (user-assigned managed identity) and the
deferred authentication-failure behavior in PACKAGE.md.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment has been minimized.

- Bump Azure.Identity 1.14.2 -> 1.21.0. The 1.14.x line is deprecated
(depends on a deprecated MSAL); 1.21.0 is the current supported
release. Verified clean restore/build across all TFMs (no NU1605,
no audit warnings) and compatible with Azure.AI.OpenAI 2.1.0.
- Extract an internal AuthenticationMode + GetAuthenticationMode seam so
the API-key-vs-DefaultAzureCredential selection is deterministically
unit-tested (upgrades the two 'only IsNotNull' tests and adds three
dedicated selection tests).
- Document DefaultAzureCredential local-dev credential-chain latency in
PACKAGE.md.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review details

  • Files reviewed: 6/6 changed files
  • Comments generated: 0 new
  • Review effort level: Low

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test quality grade — PR #9707

14 new tests graded across 1 file. 12 earn A for clean AAA structure, proper env-var isolation via [DoNotParallelize] + [TestInitialize]/[TestCleanup] snapshot/restore, and precise assertions (equality, boolean state, or exact exception type + message). 2 earn B for mixing two distinct behavioral checks in one test body — extracting the GetAuthenticationMode assertions into their own tests would sharpen those two.

GradeTestNotes
B (80–89)new AzureFoundryChatClientProviderTests.
CreateChatClientAsync_
WithApiKey_
UsesApiKeyPathAndReturnsClient
Mixes a GetAuthenticationMode equality check with IsNotNull on the client — consider separate focused tests per concern.
B (80–89)new AzureFoundryChatClientProviderTests.
CreateChatClientAsync_
WithoutApiKey_
UsesEntraPathAndReturnsClient
Dual-behavior pattern; GetAuthenticationMode(null) is already covered by a dedicated test — consider removing it from this body.
A (90–100)new AzureFoundryChatClientProviderTests.
CreateChatClientAsync_
WhenDeploymentMissing_
Throws
Exact exception type + message content verifies the deployment-missing error path. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
CreateChatClientAsync_
WhenEndpointMissing_
Throws
Exact exception type + message content verifies the endpoint-missing error path. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
GetAuthenticationMode_
WithApiKey_
ReturnsApiKey
Terse expression-bodied; precise equality confirms non-empty key routes to ApiKey mode. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
GetAuthenticationMode_
WithEmptyApiKey_
ReturnsDefaultAzureCredential
Edge case covered; empty string triggers managed identity auth identically to null. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
GetAuthenticationMode_
WithoutApiKey_
ReturnsDefaultAzureCredential
Terse; null maps to DefaultAzureCredential. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
HasToolsCapability_
ReturnsTrue
State assertion on the public contract; constant property — test guards against accidental future changes. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
IsAvailable_
WhenApiKeyAlsoSet_
ReturnsTrue
Confirms availability is unchanged when an API key is also provided. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
IsAvailable_
WhenDeploymentMissing_
ReturnsFalse
Negative test; missing deployment variable prevents availability. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
IsAvailable_
WhenEndpointAndDeploymentSetWithoutApiKey_
ReturnsTrue
Positive test for the new no-API-key availability condition. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
IsAvailable_
WhenEndpointMissing_
ReturnsFalse
Negative test; missing endpoint variable prevents availability. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
ModelName_
ReturnsDeploymentName
Clean equality assertion; deployment variable value is surfaced correctly. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
ModelName_
WhenDeploymentMissing_
ReturnsUnknown
Equality assertion covers the "unknown" fallback when no deployment is configured. No issues found.

This advisory comment was generated automatically. Grades are heuristic
and informational — they do not block merging. Re-run with
/grade-tests.

🤖 Automated content by GitHub Copilot. Generated by the Grade Tests on PR (on open / sync) workflow. · 118.1 AIC · ⌖ 10.5 AIC · ⊞ 9.5K · [◷]( · )

@Evangelink
Amaury Levé (Evangelink) enabled auto-merge (squash) July 8, 2026 07:34
@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jul 8, 2026
@Evangelink
Amaury Levé (Evangelink) merged commit d04f011 into mainJul 8, 2026
63 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/azurefoundry-managed-identity-auth branch July 8, 2026 08:03
github-actionsBot added a commit that referenced this pull request Jul 9, 2026
- AzureFoundry: add DefaultAzureCredential/managed identity auth details
and required environment variables (PR #9707)
- MSTestTestFramework: new entry documenting the native MTP ITestFramework
for MSTest introduced by RFC 018 (PRs #9706, #9743, #9748, #9755)
- VSTestBridge: note MSTest no longer depends on it on the MTP path
as of MSTest 4.3
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Amaury Levé (Evangelink) pushed a commit that referenced this pull request Jul 9, 2026
- AzureFoundry: add DefaultAzureCredential/managed identity auth details
and required environment variables (PR #9707)
- MSTestTestFramework: new entry documenting the native MTP ITestFramework
for MSTest introduced by RFC 018 (PRs #9706, #9743, #9748, #9755)
- VSTestBridge: note MSTest no longer depends on it on the MTP path
as of MSTest 4.3
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state/needs-reviewAwaiting review from the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[MTP Diagnostic Analysis] AzureFoundry hard-requires API keys and has no managed-identity auth path

3 participants

@Evangelink@0101
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Support managed identity auth in AzureFoundry provider - #9707

Merged
Amaury Levé (Evangelink) merged 3 commits into
mainfrom
dev/amauryleve/azurefoundry-managed-identity-auth
Jul 8, 2026
Merged

Support managed identity auth in AzureFoundry provider#9707
Amaury Levé (Evangelink) merged 3 commits into
mainfrom
dev/amauryleve/azurefoundry-managed-identity-auth

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Fixes#8412

Summary

Microsoft.Testing.Extensions.AzureFoundry previously hard-required AZURE_OPENAI_API_KEY and always constructed AzureOpenAIClient with ApiKeyCredential, with no managed-identity / Entra path. This made the only built-in AI provider not secure-by-default for Azure-hosted scenarios.

Changes

  • IsAvailable now only requires AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_DEPLOYMENT_NAME; the API key is no longer mandatory.
  • CreateChatClientAsync prefers an explicit AZURE_OPENAI_API_KEY when present, otherwise falls back to DefaultAzureCredential (managed identity, workload identity, Azure CLI, Visual Studio, …).
  • Added the Azure.Identity package reference (version pinned in Directory.Packages.props).
  • Updated PACKAGE.md with a configuration table documenting both authentication modes.

Notes

  • No .resx/.xlf changes were needed — the existing EnvironmentVariableNotSet string is still used for the endpoint/deployment checks.
  • Built cleanly (netstandard2.0, net8.0, net9.0) with 0 warnings/0 errors.

The AzureFoundry chat client provider now authenticates with
DefaultAzureCredential (Entra ID / managed identity) when no
AZURE_OPENAI_API_KEY is set, keeping the provider secure-by-default
for Azure-hosted scenarios. The API key remains an explicit fallback,
and IsAvailable no longer requires an API key.
Fixes#8412
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings July 7, 2026 14:04

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR makes the Microsoft.Testing.Extensions.AzureFoundry provider support Microsoft Entra ID / managed-identity authentication instead of hard-requiring AZURE_OPENAI_API_KEY, closing the secure-by-default gap described in issue #8412. The provider now reports itself available whenever the endpoint and deployment name are configured, and it authenticates via DefaultAzureCredential when no API key is supplied, keeping the explicit API-key path as an override.

Changes:

  • IsAvailable no longer requires AZURE_OPENAI_API_KEY (only endpoint + deployment name).
  • CreateChatClientAsync uses ApiKeyCredential when a key is present, otherwise falls back to DefaultAzureCredential.
  • Adds the Azure.Identity dependency (version pinned via CPM) and documents both auth modes in PACKAGE.md.
Show a summary per file
FileDescription
src/Platform/Microsoft.Testing.Extensions.AzureFoundry/OpenAIChatClientProvider.csDrops the mandatory API-key check and selects DefaultAzureCredential vs ApiKeyCredential based on whether the key is set.
src/Platform/Microsoft.Testing.Extensions.AzureFoundry/PACKAGE.mdAdds a configuration table and prose documenting the managed-identity and API-key authentication modes.
src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Microsoft.Testing.Extensions.AzureFoundry.csprojReferences the new Azure.Identity package.
Directory.Packages.propsPins Azure.Identity version 1.14.2 under central package management.

Review details

  • Files reviewed: 4/4 changed files
  • Comments generated: 1
  • Review effort level: Medium

Comment threadDirectory.Packages.props Outdated

@github-actionsgithub-actionsBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Note

🤖 Automated review by GitHub Copilot. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.

#DimensionVerdict
13Test Completeness🟡 1 MODERATE
20Build Infrastructure & Dependencies🟡 1 MODERATE
17Documentation Accuracy⚪ 1 NIT

✅ 19/22 dimensions clean (dimensions 10–12, 14, 18–19, 22 skipped as N/A).

  • Test Completeness — no unit tests cover the new DefaultAzureCredential branch (or any path of AzureOpenAIChatClientProvider)
  • Build Infrastructure — Azure.Identity adds a heavyweight transitive dependency graph; acceptable while alpha/non-shipping, but worth revisiting before GA
  • Documentation — consider noting local-dev latency from DefaultAzureCredential credential chain traversal

Overall: The implementation is clean, correct, and well-documented. The ternary conditional for credential selection is straightforward. The IsAvailable change correctly reflects the new optionality of the API key. No correctness, threading, security, or API compatibility concerns.

Comment threadDirectory.Packages.props Outdated
- Reuse a single DefaultAzureCredential instance (Lazy) so the token
cache and credential-chain discovery are shared across calls instead
of being rebuilt on every CreateChatClientAsync invocation.
- Add unit tests for the auth-selection logic (IsAvailable, ModelName,
API-key vs Entra path, missing-variable failures) in
Microsoft.Testing.Extensions.UnitTests, with env-var snapshot/restore
and DoNotParallelize; grant InternalsVisibleTo to the test assembly.
- Document AZURE_CLIENT_ID (user-assigned managed identity) and the
deferred authentication-failure behavior in PACKAGE.md.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment has been minimized.

- Bump Azure.Identity 1.14.2 -> 1.21.0. The 1.14.x line is deprecated
(depends on a deprecated MSAL); 1.21.0 is the current supported
release. Verified clean restore/build across all TFMs (no NU1605,
no audit warnings) and compatible with Azure.AI.OpenAI 2.1.0.
- Extract an internal AuthenticationMode + GetAuthenticationMode seam so
the API-key-vs-DefaultAzureCredential selection is deterministically
unit-tested (upgrades the two 'only IsNotNull' tests and adds three
dedicated selection tests).
- Document DefaultAzureCredential local-dev credential-chain latency in
PACKAGE.md.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review details

  • Files reviewed: 6/6 changed files
  • Comments generated: 0 new
  • Review effort level: Low

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test quality grade — PR #9707

14 new tests graded across 1 file. 12 earn A for clean AAA structure, proper env-var isolation via [DoNotParallelize] + [TestInitialize]/[TestCleanup] snapshot/restore, and precise assertions (equality, boolean state, or exact exception type + message). 2 earn B for mixing two distinct behavioral checks in one test body — extracting the GetAuthenticationMode assertions into their own tests would sharpen those two.

GradeTestNotes
B (80–89)new AzureFoundryChatClientProviderTests.
CreateChatClientAsync_
WithApiKey_
UsesApiKeyPathAndReturnsClient
Mixes a GetAuthenticationMode equality check with IsNotNull on the client — consider separate focused tests per concern.
B (80–89)new AzureFoundryChatClientProviderTests.
CreateChatClientAsync_
WithoutApiKey_
UsesEntraPathAndReturnsClient
Dual-behavior pattern; GetAuthenticationMode(null) is already covered by a dedicated test — consider removing it from this body.
A (90–100)new AzureFoundryChatClientProviderTests.
CreateChatClientAsync_
WhenDeploymentMissing_
Throws
Exact exception type + message content verifies the deployment-missing error path. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
CreateChatClientAsync_
WhenEndpointMissing_
Throws
Exact exception type + message content verifies the endpoint-missing error path. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
GetAuthenticationMode_
WithApiKey_
ReturnsApiKey
Terse expression-bodied; precise equality confirms non-empty key routes to ApiKey mode. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
GetAuthenticationMode_
WithEmptyApiKey_
ReturnsDefaultAzureCredential
Edge case covered; empty string triggers managed identity auth identically to null. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
GetAuthenticationMode_
WithoutApiKey_
ReturnsDefaultAzureCredential
Terse; null maps to DefaultAzureCredential. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
HasToolsCapability_
ReturnsTrue
State assertion on the public contract; constant property — test guards against accidental future changes. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
IsAvailable_
WhenApiKeyAlsoSet_
ReturnsTrue
Confirms availability is unchanged when an API key is also provided. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
IsAvailable_
WhenDeploymentMissing_
ReturnsFalse
Negative test; missing deployment variable prevents availability. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
IsAvailable_
WhenEndpointAndDeploymentSetWithoutApiKey_
ReturnsTrue
Positive test for the new no-API-key availability condition. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
IsAvailable_
WhenEndpointMissing_
ReturnsFalse
Negative test; missing endpoint variable prevents availability. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
ModelName_
ReturnsDeploymentName
Clean equality assertion; deployment variable value is surfaced correctly. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
ModelName_
WhenDeploymentMissing_
ReturnsUnknown
Equality assertion covers the "unknown" fallback when no deployment is configured. No issues found.

This advisory comment was generated automatically. Grades are heuristic
and informational — they do not block merging. Re-run with
/grade-tests.

🤖 Automated content by GitHub Copilot. Generated by the Grade Tests on PR (on open / sync) workflow. · 118.1 AIC · ⌖ 10.5 AIC · ⊞ 9.5K · [◷]( · )

@Evangelink
Amaury Levé (Evangelink) enabled auto-merge (squash) July 8, 2026 07:34
@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jul 8, 2026
@Evangelink
Amaury Levé (Evangelink) merged commit d04f011 into mainJul 8, 2026
63 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/azurefoundry-managed-identity-auth branch July 8, 2026 08:03
github-actionsBot added a commit that referenced this pull request Jul 9, 2026
- AzureFoundry: add DefaultAzureCredential/managed identity auth details
and required environment variables (PR #9707)
- MSTestTestFramework: new entry documenting the native MTP ITestFramework
for MSTest introduced by RFC 018 (PRs #9706, #9743, #9748, #9755)
- VSTestBridge: note MSTest no longer depends on it on the MTP path
as of MSTest 4.3
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Amaury Levé (Evangelink) pushed a commit that referenced this pull request Jul 9, 2026
- AzureFoundry: add DefaultAzureCredential/managed identity auth details
and required environment variables (PR #9707)
- MSTestTestFramework: new entry documenting the native MTP ITestFramework
for MSTest introduced by RFC 018 (PRs #9706, #9743, #9748, #9755)
- VSTestBridge: note MSTest no longer depends on it on the MTP path
as of MSTest 4.3
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state/needs-reviewAwaiting review from the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[MTP Diagnostic Analysis] AzureFoundry hard-requires API keys and has no managed-identity auth path

3 participants

@Evangelink@0101
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Support managed identity auth in AzureFoundry provider - #9707

Merged
Amaury Levé (Evangelink) merged 3 commits into
mainfrom
dev/amauryleve/azurefoundry-managed-identity-auth
Jul 8, 2026
Merged

Support managed identity auth in AzureFoundry provider#9707
Amaury Levé (Evangelink) merged 3 commits into
mainfrom
dev/amauryleve/azurefoundry-managed-identity-auth

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Fixes#8412

Summary

Microsoft.Testing.Extensions.AzureFoundry previously hard-required AZURE_OPENAI_API_KEY and always constructed AzureOpenAIClient with ApiKeyCredential, with no managed-identity / Entra path. This made the only built-in AI provider not secure-by-default for Azure-hosted scenarios.

Changes

  • IsAvailable now only requires AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_DEPLOYMENT_NAME; the API key is no longer mandatory.
  • CreateChatClientAsync prefers an explicit AZURE_OPENAI_API_KEY when present, otherwise falls back to DefaultAzureCredential (managed identity, workload identity, Azure CLI, Visual Studio, …).
  • Added the Azure.Identity package reference (version pinned in Directory.Packages.props).
  • Updated PACKAGE.md with a configuration table documenting both authentication modes.

Notes

  • No .resx/.xlf changes were needed — the existing EnvironmentVariableNotSet string is still used for the endpoint/deployment checks.
  • Built cleanly (netstandard2.0, net8.0, net9.0) with 0 warnings/0 errors.

The AzureFoundry chat client provider now authenticates with
DefaultAzureCredential (Entra ID / managed identity) when no
AZURE_OPENAI_API_KEY is set, keeping the provider secure-by-default
for Azure-hosted scenarios. The API key remains an explicit fallback,
and IsAvailable no longer requires an API key.
Fixes#8412
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings July 7, 2026 14:04

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR makes the Microsoft.Testing.Extensions.AzureFoundry provider support Microsoft Entra ID / managed-identity authentication instead of hard-requiring AZURE_OPENAI_API_KEY, closing the secure-by-default gap described in issue #8412. The provider now reports itself available whenever the endpoint and deployment name are configured, and it authenticates via DefaultAzureCredential when no API key is supplied, keeping the explicit API-key path as an override.

Changes:

  • IsAvailable no longer requires AZURE_OPENAI_API_KEY (only endpoint + deployment name).
  • CreateChatClientAsync uses ApiKeyCredential when a key is present, otherwise falls back to DefaultAzureCredential.
  • Adds the Azure.Identity dependency (version pinned via CPM) and documents both auth modes in PACKAGE.md.
Show a summary per file
FileDescription
src/Platform/Microsoft.Testing.Extensions.AzureFoundry/OpenAIChatClientProvider.csDrops the mandatory API-key check and selects DefaultAzureCredential vs ApiKeyCredential based on whether the key is set.
src/Platform/Microsoft.Testing.Extensions.AzureFoundry/PACKAGE.mdAdds a configuration table and prose documenting the managed-identity and API-key authentication modes.
src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Microsoft.Testing.Extensions.AzureFoundry.csprojReferences the new Azure.Identity package.
Directory.Packages.propsPins Azure.Identity version 1.14.2 under central package management.

Review details

  • Files reviewed: 4/4 changed files
  • Comments generated: 1
  • Review effort level: Medium

Comment threadDirectory.Packages.props Outdated

@github-actionsgithub-actionsBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Note

🤖 Automated review by GitHub Copilot. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.

#DimensionVerdict
13Test Completeness🟡 1 MODERATE
20Build Infrastructure & Dependencies🟡 1 MODERATE
17Documentation Accuracy⚪ 1 NIT

✅ 19/22 dimensions clean (dimensions 10–12, 14, 18–19, 22 skipped as N/A).

  • Test Completeness — no unit tests cover the new DefaultAzureCredential branch (or any path of AzureOpenAIChatClientProvider)
  • Build Infrastructure — Azure.Identity adds a heavyweight transitive dependency graph; acceptable while alpha/non-shipping, but worth revisiting before GA
  • Documentation — consider noting local-dev latency from DefaultAzureCredential credential chain traversal

Overall: The implementation is clean, correct, and well-documented. The ternary conditional for credential selection is straightforward. The IsAvailable change correctly reflects the new optionality of the API key. No correctness, threading, security, or API compatibility concerns.

Comment threadDirectory.Packages.props Outdated
- Reuse a single DefaultAzureCredential instance (Lazy) so the token
cache and credential-chain discovery are shared across calls instead
of being rebuilt on every CreateChatClientAsync invocation.
- Add unit tests for the auth-selection logic (IsAvailable, ModelName,
API-key vs Entra path, missing-variable failures) in
Microsoft.Testing.Extensions.UnitTests, with env-var snapshot/restore
and DoNotParallelize; grant InternalsVisibleTo to the test assembly.
- Document AZURE_CLIENT_ID (user-assigned managed identity) and the
deferred authentication-failure behavior in PACKAGE.md.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment has been minimized.

- Bump Azure.Identity 1.14.2 -> 1.21.0. The 1.14.x line is deprecated
(depends on a deprecated MSAL); 1.21.0 is the current supported
release. Verified clean restore/build across all TFMs (no NU1605,
no audit warnings) and compatible with Azure.AI.OpenAI 2.1.0.
- Extract an internal AuthenticationMode + GetAuthenticationMode seam so
the API-key-vs-DefaultAzureCredential selection is deterministically
unit-tested (upgrades the two 'only IsNotNull' tests and adds three
dedicated selection tests).
- Document DefaultAzureCredential local-dev credential-chain latency in
PACKAGE.md.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review details

  • Files reviewed: 6/6 changed files
  • Comments generated: 0 new
  • Review effort level: Low

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test quality grade — PR #9707

14 new tests graded across 1 file. 12 earn A for clean AAA structure, proper env-var isolation via [DoNotParallelize] + [TestInitialize]/[TestCleanup] snapshot/restore, and precise assertions (equality, boolean state, or exact exception type + message). 2 earn B for mixing two distinct behavioral checks in one test body — extracting the GetAuthenticationMode assertions into their own tests would sharpen those two.

GradeTestNotes
B (80–89)new AzureFoundryChatClientProviderTests.
CreateChatClientAsync_
WithApiKey_
UsesApiKeyPathAndReturnsClient
Mixes a GetAuthenticationMode equality check with IsNotNull on the client — consider separate focused tests per concern.
B (80–89)new AzureFoundryChatClientProviderTests.
CreateChatClientAsync_
WithoutApiKey_
UsesEntraPathAndReturnsClient
Dual-behavior pattern; GetAuthenticationMode(null) is already covered by a dedicated test — consider removing it from this body.
A (90–100)new AzureFoundryChatClientProviderTests.
CreateChatClientAsync_
WhenDeploymentMissing_
Throws
Exact exception type + message content verifies the deployment-missing error path. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
CreateChatClientAsync_
WhenEndpointMissing_
Throws
Exact exception type + message content verifies the endpoint-missing error path. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
GetAuthenticationMode_
WithApiKey_
ReturnsApiKey
Terse expression-bodied; precise equality confirms non-empty key routes to ApiKey mode. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
GetAuthenticationMode_
WithEmptyApiKey_
ReturnsDefaultAzureCredential
Edge case covered; empty string triggers managed identity auth identically to null. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
GetAuthenticationMode_
WithoutApiKey_
ReturnsDefaultAzureCredential
Terse; null maps to DefaultAzureCredential. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
HasToolsCapability_
ReturnsTrue
State assertion on the public contract; constant property — test guards against accidental future changes. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
IsAvailable_
WhenApiKeyAlsoSet_
ReturnsTrue
Confirms availability is unchanged when an API key is also provided. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
IsAvailable_
WhenDeploymentMissing_
ReturnsFalse
Negative test; missing deployment variable prevents availability. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
IsAvailable_
WhenEndpointAndDeploymentSetWithoutApiKey_
ReturnsTrue
Positive test for the new no-API-key availability condition. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
IsAvailable_
WhenEndpointMissing_
ReturnsFalse
Negative test; missing endpoint variable prevents availability. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
ModelName_
ReturnsDeploymentName
Clean equality assertion; deployment variable value is surfaced correctly. No issues found.
A (90–100)new AzureFoundryChatClientProviderTests.
ModelName_
WhenDeploymentMissing_
ReturnsUnknown
Equality assertion covers the "unknown" fallback when no deployment is configured. No issues found.

This advisory comment was generated automatically. Grades are heuristic
and informational — they do not block merging. Re-run with
/grade-tests.

🤖 Automated content by GitHub Copilot. Generated by the Grade Tests on PR (on open / sync) workflow. · 118.1 AIC · ⌖ 10.5 AIC · ⊞ 9.5K · [◷]( · )

@Evangelink
Amaury Levé (Evangelink) enabled auto-merge (squash) July 8, 2026 07:34
@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jul 8, 2026
@Evangelink
Amaury Levé (Evangelink) merged commit d04f011 into mainJul 8, 2026
63 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/azurefoundry-managed-identity-auth branch July 8, 2026 08:03
github-actionsBot added a commit that referenced this pull request Jul 9, 2026
- AzureFoundry: add DefaultAzureCredential/managed identity auth details
and required environment variables (PR #9707)
- MSTestTestFramework: new entry documenting the native MTP ITestFramework
for MSTest introduced by RFC 018 (PRs #9706, #9743, #9748, #9755)
- VSTestBridge: note MSTest no longer depends on it on the MTP path
as of MSTest 4.3
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Amaury Levé (Evangelink) pushed a commit that referenced this pull request Jul 9, 2026
- AzureFoundry: add DefaultAzureCredential/managed identity auth details
and required environment variables (PR #9707)
- MSTestTestFramework: new entry documenting the native MTP ITestFramework
for MSTest introduced by RFC 018 (PRs #9706, #9743, #9748, #9755)
- VSTestBridge: note MSTest no longer depends on it on the MTP path
as of MSTest 4.3
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state/needs-reviewAwaiting review from the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[MTP Diagnostic Analysis] AzureFoundry hard-requires API keys and has no managed-identity auth path

3 participants

@Evangelink@0101