[release/11.0 backport] Use resolved service connection ID for Helix authentication - #17589
missymessa wants to merge 4 commits into
Conversation
Prefer the workload identity assertion supplied by AzureCLI for Helix SDK operations while preserving existing credential ordering for other Arcade consumers. AB#12269 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3ee73e53-8098-4533-b3f9-1686640d4f55
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved credential-protection, cleanup, credential-selection, and assertion-lifetime issues remain.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Updates Helix submissions to use AzureCLI-issued Microsoft Entra workload identity assertions while preserving existing credential behavior elsewhere.
Changes:
- Adds workload-identity credential selection, fallback logic, and tests.
- Stages assertions in restricted temporary files for Helix processes.
- Documents WIF requirements and maintains source-build compatibility.
File summaries
| File | Summary |
|---|---|
src/Microsoft.DotNet.Helix/Sdk/HelixTask.cs |
Prefers workload identity; Moderate (1 vote): a static assertion may expire during long-running polling. |
src/Microsoft.DotNet.Helix/Sdk.Tests/Microsoft.DotNet.Helix.Sdk.Tests/Microsoft.DotNet.Helix.Sdk.Tests.csproj |
Adds test references and source-build exclusions. |
src/Microsoft.DotNet.Helix/Sdk.Tests/Microsoft.DotNet.Helix.Sdk.Tests/DefaultIdentityTokenCredentialTests.cs |
Tests credential precedence and fallback behavior. |
src/Microsoft.DotNet.ArcadeAzureIntegration/Microsoft.DotNet.ArcadeAzureIntegration.csproj |
Grants test access to internal credential-selection APIs. |
src/Microsoft.DotNet.ArcadeAzureIntegration/DefaultIdentityTokenCredentialOptions.cs |
Adds workload-identity preference configuration. |
src/Microsoft.DotNet.ArcadeAzureIntegration/DefaultIdentityTokenCredential.cs |
Supports token-file selection; Moderate (1 vote): discovery can precede the Azure Pipelines fast path and leave cached assertion material. |
eng/common/core-templates/steps/send-to-helix.yml |
Manages assertion files; Critical (1 vote): ACL failures do not fail closed. Moderate (1 vote): cleanup failures are silently hidden. |
Documentation/AzureDevOps/SendingJobsToHelix.md |
Documents Entra/WIF setup and assertion handling. |
Review details
Suppressed comments (4)
eng/common/core-templates/steps/send-to-helix.yml:78
- The cleanup step relies on
HelixEntraTokenFile, but that variable is published only after the assertion has been written. If this finalWriteAllTextfails after creating the file, thealways()cleanup task has no path and can leave the assertion in the agent temp directory. Publish the path before the assertion write (or use afinally) so cleanup also covers write failures.
Write-Host "##vso[task.setvariable variable=HelixEntraTokenFile]$tokenFile"
eng/common/core-templates/steps/send-to-helix.yml:177
SilentlyContinuehides deletion failures, so a locked or inaccessible token file can retain the static assertion without failing or warning the job. Since this file is the credential material, cleanup should surface removal failures instead of silently leaving it behind.
- pwsh: Remove-Item -LiteralPath "$(HelixEntraTokenFile)" -Force -ErrorAction SilentlyContinue
src/Microsoft.DotNet.ArcadeAzureIntegration/DefaultIdentityTokenCredential.cs:45
- This now discovers the legacy
servicePrincipalId/idTokenworkload credential before the Azure Pipelines fast path. As a result, non-Helix consumers running underAzureCLI@2can write the raw assertion to the cached temp file even whenAzurePipelinesCredentialis selected, and that file has no cleanup path; defer this discovery until after the existing Azure Pipelines early return whenPreferWorkloadIdentityCredentialis false.
TokenCredential? workloadIdentityCredential = GetWorkloadIdentityCredentialForAzurePipelineTask();
src/Microsoft.DotNet.Helix/Sdk/HelixTask.cs:102
- This selects a
WorkloadIdentityCredentialbacked by the singleidTokensnapshot written by the AzureCLI step. The normal template waits for work-item completion and the SDK polls without a maximum, so a queued or long-running job can outlive that assertion; refreshing the access token then reuses an expired assertion and fails, unlike the previous Azure Pipelines credential's assertion renewal. Please provide assertion renewal or keep this path limited to operations that cannot outlive the assertion.
new DefaultIdentityTokenCredential(new DefaultIdentityTokenCredentialOptions
{
PreferWorkloadIdentityCredential = true,
- Files reviewed: 8/8 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Forward the endpoint GUID exposed by AzureCLI so AzurePipelinesCredential can request renewable assertions for both Entra-issued and legacy WIF service connections. AB#12269 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3ee73e53-8098-4533-b3f9-1686640d4f55
There was a problem hiding this comment.
🟡 Changes recommended
Both AzureCLI tasks must retain addSpnToEnvironment before approval.
Get a fresh assessment by requesting another Copilot review.
Review details
- Files reviewed: 3/3 changed files
- Comments generated: 3
- Review effort level: Lite
Use the workload identity federation service connection name consistently in both examples. AB#12269 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: b83ddd94-a1da-4c3c-aead-1d1643a6b83f
There was a problem hiding this comment.
🟢 Approval recommended
The remaining documentation comments are minor nits and do not block approval.
Review details
Suppressed comments (2)
Documentation/AzureDevOps/SendingJobsToHelix.md:68
- This changes the documented contract from an endpoint ID to a workload-identity service-connection name, but the SDK README's parameter reference still says
azureSubscriptionis an ID (src/Microsoft.DotNet.Helix/Sdk/Readme.md:90). Update that user-facing reference in the same change so callers do not receive conflicting instructions about which value to pass.
The service connection must use workload identity federation. Azure DevOps resolves the service connection name to its endpoint ID for `AzureCLI@2`, and the templates forward that resolved ID to `AzurePipelinesCredential`. The OIDC token endpoint requires the endpoint ID rather than the display name. This allows the credential to request renewable assertions using either legacy Azure DevOps-issued or newer Microsoft Entra-issued service connections.
eng/common/core-templates/job/helix-job-monitor.yml:67
- This template now requires the service-connection name, but the package's monitor documentation still describes
azureSubscriptionas an ID atsrc/Microsoft.DotNet.Helix/Sdk/Readme.md:90. Users following that public documentation can continue supplying the old value; update that reference to explain the WIF service-connection name and AzureCLI endpoint-ID resolution.
# Workload identity federation service connection name authorized for Helix. Required when
# useEntraAuthentication is true.
- Files reviewed: 3/3 changed files
- Comments generated: 0 new
- Review effort level: Lite
Staging validationThe PAT-free Entra authentication path succeeded end to end in Helix Machines build 3080549:
The overall Helix Machines build was partially succeeded only because the unrelated Smart CI telemetry task failed. The targeted Helix validation was successful. Arcade CI for this PR is also fully green, including |
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
The submission and monitoring paths still use an authentication flow that can fail for Microsoft Entra-issued WIF connections.
Get a fresh assessment by requesting another Copilot review.
Review effort: Lite
Findings: 2
| AZURESUBSCRIPTION_CLIENT_ID: $(HelixEntraClientId) | ||
| AZURESUBSCRIPTION_TENANT_ID: $(HelixEntraTenantId) | ||
| AZURESUBSCRIPTION_SERVICE_CONNECTION_ID: ${{ parameters.azureSubscription }} | ||
| AZURESUBSCRIPTION_SERVICE_CONNECTION_ID: $(HelixEntraServiceConnectionId) |
| AZURESUBSCRIPTION_CLIENT_ID: $(HelixEntraClientId) | ||
| AZURESUBSCRIPTION_TENANT_ID: $(HelixEntraTenantId) | ||
| AZURESUBSCRIPTION_SERVICE_CONNECTION_ID: ${{ parameters.HelixAzureSubscription }} | ||
| AZURESUBSCRIPTION_SERVICE_CONNECTION_ID: $(HelixEntraServiceConnectionId) |
|
|
||
| Set `HelixUseEntraAuthentication` to `true` and pass an Azure service connection authorized for Helix through `HelixAzureSubscription`. These parameters configure the `send-to-helix.yml` steps template and the SDK tasks that submit jobs. | ||
|
|
||
| The service connection must use workload identity federation. Azure DevOps resolves the service connection name to its endpoint ID for `AzureCLI@2`, and the templates forward that resolved ID to `AzurePipelinesCredential`. The OIDC token endpoint requires the endpoint ID rather than the display name. This allows the credential to request renewable assertions using either legacy Azure DevOps-issued or newer Microsoft Entra-issued service connections. |


Backport sequencing
Backport of #17592. Keep this PR in draft and do not merge it until the change has landed in
main.Summary
AzureCLI@2.AzurePipelinesCredential.send-to-helixand the standalone Helix Job Monitor.Why
Helix Machines staging build 3079813 showed that the AzureCLI task authenticated successfully, but the later Helix process received the service connection display name as
AZURESUBSCRIPTION_SERVICE_CONNECTION_ID. The Azure DevOps OIDC endpoint requires the endpoint GUID, soAzurePipelinesCredentialobtained an assertion from the wrong issuer and failed withAADSTS700211against the Microsoft Entra-issued federation record.AzureCLI@2exposes the resolved endpoint GUID asAZURESUBSCRIPTION_SERVICE_CONNECTION_ID. These templates now capture that value and forward it to the Helix process.AzurePipelinesCredentialcan then request fresh assertions for long-running inline waits and the six-hour monitor without persisting assertion material.Compatibility
HelixUseEntraAuthenticationanduseEntraAuthenticationremain opt-in.Validation
AB#12269