Skip to content

[release/11.0 backport] Use resolved service connection ID for Helix authentication - #17589

Draft
missymessa wants to merge 4 commits into
release/11.0from
fix/12269-entra-service-connection-token
Draft

missymessa wants to merge 4 commits into
release/11.0from
fix/12269-entra-service-connection-token

Conversation

@missymessa

@missymessa missymessa commented Sep 18, 2026 •

Copy link
Copy Markdown
Member

Backport sequencing

Backport of #17592. Keep this PR in draft and do not merge it until the change has landed in main.

Summary

  • Resolve the configured Helix service connection name to its endpoint GUID through AzureCLI@2.
  • Pass the resolved client, tenant, and endpoint IDs to AzurePipelinesCredential.
  • Apply the same renewable authentication path to both send-to-helix and the standalone Helix Job Monitor.
  • Preserve existing PAT and anonymous behavior.

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, so AzurePipelinesCredential obtained an assertion from the wrong issuer and failed with AADSTS700211 against the Microsoft Entra-issued federation record.

AzureCLI@2 exposes the resolved endpoint GUID as AZURESUBSCRIPTION_SERVICE_CONNECTION_ID. These templates now capture that value and forward it to the Helix process. AzurePipelinesCredential can then request fresh assertions for long-running inline waits and the six-hour monitor without persisting assertion material.

Compatibility

  • HelixUseEntraAuthentication and useEntraAuthentication remain opt-in.
  • PAT and anonymous paths are unchanged.
  • Both legacy Azure DevOps-issued and newer Microsoft Entra-issued WIF service connections are supported.

Validation

  • Full Arcade Release build succeeded with 0 warnings and 0 errors.
  • Independent source review verified AzureCLI variable availability, job scoping, and existing credential renewal behavior.
  • The Helix Machines staging canary will validate the resolved endpoint flow end to end.

AB#12269

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
Copilot AI lite review requested due to automatic review settings September 18, 2026 16:49

Copilot AI 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.

🟡 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 final WriteAllText fails after creating the file, the always() cleanup task has no path and can leave the assertion in the agent temp directory. Publish the path before the assertion write (or use a finally) so cleanup also covers write failures.
          Write-Host "##vso[task.setvariable variable=HelixEntraTokenFile]$tokenFile"

eng/common/core-templates/steps/send-to-helix.yml:177

  • SilentlyContinue hides 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/idToken workload credential before the Azure Pipelines fast path. As a result, non-Helix consumers running under AzureCLI@2 can write the raw assertion to the cached temp file even when AzurePipelinesCredential is selected, and that file has no cleanup path; defer this discovery until after the existing Azure Pipelines early return when PreferWorkloadIdentityCredential is false.
        TokenCredential? workloadIdentityCredential = GetWorkloadIdentityCredentialForAzurePipelineTask();

src/Microsoft.DotNet.Helix/Sdk/HelixTask.cs:102

  • This selects a WorkloadIdentityCredential backed by the single idToken snapshot 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.

Comment thread eng/common/core-templates/steps/send-to-helix.yml Outdated
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
Copilot AI review requested due to automatic review settings September 18, 2026 17:24
@missymessa missymessa changed the title Use Entra-issued assertion for Helix submissions Use resolved service connection ID for Helix authentication Sep 18, 2026

Copilot AI 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.

🟡 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

Comment thread eng/common/core-templates/job/helix-job-monitor.yml
Comment thread eng/common/core-templates/steps/send-to-helix.yml
Comment thread Documentation/AzureDevOps/SendingJobsToHelix.md
@missymessa
missymessa changed the base branch from release/11.0 to main September 18, 2026 17:39
@missymessa
missymessa changed the base branch from main to release/11.0 September 18, 2026 17:39
@missymessa missymessa changed the title Use resolved service connection ID for Helix authentication [release/11.0 backport] Use resolved service connection ID for Helix authentication Sep 18, 2026
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
Copilot AI review requested due to automatic review settings September 18, 2026 17:48

Copilot AI 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.

🟢 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 azureSubscription is 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 azureSubscription as an ID at src/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

@missymessa

Copy link
Copy Markdown
Member Author

Staging validation

The PAT-free Entra authentication path succeeded end to end in Helix Machines build 3080549:

  • AzureCLI workload-identity initialization succeeded.
  • The service-connection endpoint GUID was passed to the existing renewable AzurePipelinesCredential path.
  • Helix accepted and completed staging job be228d4c-3c51-4cde-859e-cb048e781fd6 without a PAT.
  • Submission, inline wait, and result collection completed with 0 warnings and 0 errors.

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 Test Monitor Helix Jobs.

Copilot AI review requested due to automatic review settings September 23, 2026 21:19

Copilot AI 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.

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 Medium severity · 1 Low severity

Open (3)

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.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants