From 5bc41b2e43c444e33341a56d5952a1cda567a9ea Mon Sep 17 00:00:00 2001 From: Missy Messa Date: Tue, 7 Apr 2026 08:26:54 -0700 Subject: [PATCH 1/2] Use Entra credential for symbol upload, removing dnceng-symbol-server-pat dependency When TempSymbolsAzureDevOpsOrgToken is not provided, use DefaultIdentityTokenCredential (the same credential already used for symbol promotion) instead of PATCredential for symbol uploads. This enables the pipeline to use the AzureCLI@2 task's federated identity (maestro-build-promotion) for symbol management, eliminating the need for the dnceng-symbol-server-pat PAT. - PublishArtifactsInManifestBase.cs: Fall back to DefaultIdentityTokenCredential when TempSymbolsAzureDevOpsOrgToken is empty/null; retain PATCredential for backward compat - publish.yml: Remove DotNet-Symbol-Server-Pats variable group and TempSymbolsAzureDevOpsOrgToken - publish-logs.yml: Remove dnceng-symbol-server-pat from redaction list Fixes: AB#10150 --- eng/common/core-templates/steps/publish-logs.yml | 1 - eng/publishing/v3/publish.yml | 2 -- .../src/PublishArtifactsInManifestBase.cs | 11 ++++++++++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/eng/common/core-templates/steps/publish-logs.yml b/eng/common/core-templates/steps/publish-logs.yml index 84a1922c73f..5ec863e5fa5 100644 --- a/eng/common/core-templates/steps/publish-logs.yml +++ b/eng/common/core-templates/steps/publish-logs.yml @@ -35,7 +35,6 @@ steps: '$(akams-client-id)' '$(microsoft-symbol-server-pat)' '$(symweb-symbol-server-pat)' - '$(dnceng-symbol-server-pat)' '$(dn-bot-all-orgs-build-rw-code-rw)' '$(System.AccessToken)' ${{parameters.CustomSensitiveDataList}} diff --git a/eng/publishing/v3/publish.yml b/eng/publishing/v3/publish.yml index 383ac1b8e5d..bad62654e07 100644 --- a/eng/publishing/v3/publish.yml +++ b/eng/publishing/v3/publish.yml @@ -12,7 +12,6 @@ stages: displayName: Publish Assets and Symbols timeoutInMinutes: 120 variables: - - group: DotNet-Symbol-Server-Pats - group: AzureDevOps-Artifact-Feeds-Pats - group: Publish-Build-Assets @@ -157,7 +156,6 @@ stages: /p:PDBArtifactsBasePath='$(Build.ArtifactStagingDirectory)/PDBArtifacts/' /p:SymbolPublishingExclusionsFile='$(Build.ArtifactStagingDirectory)/ReleaseConfigs/SymbolPublishingExclusionsFile.txt' /p:TempSymbolsAzureDevOpsOrg='dnceng' - /p:TempSymbolsAzureDevOpsOrgToken='$(dnceng-symbol-server-pat)' /p:SymbolRequestProject='dotnet' ${{ parameters.symbolPublishingAdditionalParameters}} /p:BuildQuality='${{ parameters.buildQuality }}' diff --git a/src/Microsoft.DotNet.Build.Tasks.Feed/src/PublishArtifactsInManifestBase.cs b/src/Microsoft.DotNet.Build.Tasks.Feed/src/PublishArtifactsInManifestBase.cs index 90f94d2cee9..dd418c315a0 100644 --- a/src/Microsoft.DotNet.Build.Tasks.Feed/src/PublishArtifactsInManifestBase.cs +++ b/src/Microsoft.DotNet.Build.Tasks.Feed/src/PublishArtifactsInManifestBase.cs @@ -22,6 +22,7 @@ using Microsoft.Arcade.Common; using Microsoft.Build.Framework; using Microsoft.DotNet.Build.Tasks.Feed.Model; +using Azure.Core; using Azure.Identity; using Microsoft.DotNet.ProductConstructionService.Client; using Microsoft.DotNet.ProductConstructionService.Client.Models; @@ -718,7 +719,15 @@ public async Task HandleSymbolPublishingAsync( Task CreatePublishSymbolHelper(string symbolPublishingExclusionsFile, bool publishSpecialClrFiles, bool dryRun) { FrozenSet exclusions = LoadExclusions(symbolPublishingExclusionsFile); - PATCredential creds = new(TempSymbolsAzureDevOpsOrgToken); + + TokenCredential creds = string.IsNullOrEmpty(TempSymbolsAzureDevOpsOrgToken) + ? new DefaultIdentityTokenCredential( + new DefaultIdentityTokenCredentialOptions + { + ManagedIdentityClientId = ManagedIdentityClientId + }) + : new PATCredential(TempSymbolsAzureDevOpsOrgToken); + TaskTracer tracer = new(Log, verbose: true); SymbolPublisherOptions options = new( From 16b33ca42fbe29e3d73f790544fea992b9c53ac5 Mon Sep 17 00:00:00 2001 From: Missy Messa Date: Fri, 10 Apr 2026 13:53:38 -0700 Subject: [PATCH 2/2] Add DefaultIdentityTokenCredential fallback for symbol publishing and regression tests (WI 10150) --- .../PublishToSymbolServerTest.cs | 61 ++++++++++++++++++- .../src/PublishArtifactsInManifestBase.cs | 27 +++++--- 2 files changed, 76 insertions(+), 12 deletions(-) diff --git a/src/Microsoft.DotNet.Build.Tasks.Feed.Tests/PublishToSymbolServerTest.cs b/src/Microsoft.DotNet.Build.Tasks.Feed.Tests/PublishToSymbolServerTest.cs index 42fe709951a..ce7adab0a9a 100644 --- a/src/Microsoft.DotNet.Build.Tasks.Feed.Tests/PublishToSymbolServerTest.cs +++ b/src/Microsoft.DotNet.Build.Tasks.Feed.Tests/PublishToSymbolServerTest.cs @@ -186,7 +186,63 @@ await task.HandleSymbolPublishingAsync( Assert.Contains("to last 3650 days", registerLog.Message); } - private static (MockBuildEngine, PublishArtifactsInManifestV3, ReadOnlyDictionary, string, string, ProductConstructionService.Client.Models.Build) GetCanonicalSymbolTestAssets(SymbolPublishVisibility targetServer = SymbolPublishVisibility.Public) + [Fact] + public async Task PublishSymbolsWithPatDoesNotLogDefaultIdentityFallback() + { + (var buildEngine, var task, var symbolPackages, var symbolFilesDir, var exclusionFile, var buildInfo) = + GetCanonicalSymbolTestAssets(); + + try + { + await task.HandleSymbolPublishingAsync( + buildInfo: buildInfo, + symbolPackages, + pdbArtifactsBasePath: symbolFilesDir, + symbolPublishingExclusionsFile: exclusionFile, + publishSpecialClrFiles: false, + clientThrottle: null, + dryRun: true, + Internal.SymbolHelper.SymbolPromotionHelper.Environment.PPE); + } + catch (InvalidOperationException ex) when (ex.Message.Contains("Windows x64 hosting")) + { + } + + Assert.DoesNotContain(buildEngine.BuildMessageEvents, x => x.Message.Contains("Using DefaultIdentityTokenCredential for temporary symbol publishing")); + } + + [Fact] + public async Task PublishSymbolsWithoutPatLogsDefaultIdentityFallback() + { + (var buildEngine, var task, var symbolPackages, var symbolFilesDir, var exclusionFile, var buildInfo) = + GetCanonicalSymbolTestAssets( + SymbolPublishVisibility.Public, + tempSymbolsAzureDevOpsOrgToken: null, + managedIdentityClientId: "11111111-1111-1111-1111-111111111111"); + + try + { + await task.HandleSymbolPublishingAsync( + buildInfo: buildInfo, + symbolPackages, + pdbArtifactsBasePath: symbolFilesDir, + symbolPublishingExclusionsFile: exclusionFile, + publishSpecialClrFiles: false, + clientThrottle: null, + dryRun: true, + Internal.SymbolHelper.SymbolPromotionHelper.Environment.PPE); + } + catch (InvalidOperationException ex) when (ex.Message.Contains("Windows x64 hosting")) + { + } + + Assert.Contains(buildEngine.BuildMessageEvents, x => x.Message.Contains("Using DefaultIdentityTokenCredential for temporary symbol publishing")); + } + + private static (MockBuildEngine, PublishArtifactsInManifestV3, ReadOnlyDictionary, string, string, ProductConstructionService.Client.Models.Build) GetCanonicalSymbolTestAssets( + SymbolPublishVisibility targetServer = SymbolPublishVisibility.Public, + string tempSymbolsAzureDevOpsOrgToken = "token", + string managedIdentityClientId = null) { const string symbolPackageName= "test-package-a.1.0.0.symbols.nupkg"; @@ -221,8 +277,9 @@ private static (MockBuildEngine, PublishArtifactsInManifestV3, ReadOnlyDictionar BuildEngine = buildEngine, ArtifactsBasePath = "testPath", BlobAssetsBasePath = symbolFilesDir, + ManagedIdentityClientId = managedIdentityClientId, TempSymbolsAzureDevOpsOrg = "dncengtest", - TempSymbolsAzureDevOpsOrgToken = "token", + TempSymbolsAzureDevOpsOrgToken = tempSymbolsAzureDevOpsOrgToken, SymbolRequestProject = "dotnettest" }; task.FeedConfigs.Add(TargetFeedContentType.Symbols, feedConfigsForSymbols); diff --git a/src/Microsoft.DotNet.Build.Tasks.Feed/src/PublishArtifactsInManifestBase.cs b/src/Microsoft.DotNet.Build.Tasks.Feed/src/PublishArtifactsInManifestBase.cs index dd418c315a0..5939733e190 100644 --- a/src/Microsoft.DotNet.Build.Tasks.Feed/src/PublishArtifactsInManifestBase.cs +++ b/src/Microsoft.DotNet.Build.Tasks.Feed/src/PublishArtifactsInManifestBase.cs @@ -719,20 +719,12 @@ public async Task HandleSymbolPublishingAsync( Task CreatePublishSymbolHelper(string symbolPublishingExclusionsFile, bool publishSpecialClrFiles, bool dryRun) { FrozenSet exclusions = LoadExclusions(symbolPublishingExclusionsFile); - - TokenCredential creds = string.IsNullOrEmpty(TempSymbolsAzureDevOpsOrgToken) - ? new DefaultIdentityTokenCredential( - new DefaultIdentityTokenCredentialOptions - { - ManagedIdentityClientId = ManagedIdentityClientId - }) - : new PATCredential(TempSymbolsAzureDevOpsOrgToken); - TaskTracer tracer = new(Log, verbose: true); + TokenCredential symbolUploadCredential = GetTemporarySymbolCredential(); SymbolPublisherOptions options = new( TempSymbolsAzureDevOpsOrg, - creds, + symbolUploadCredential, packageFileExcludeList: exclusions, convertPortablePdbs: false, treatPdbConversionIssuesAsInfo: false, @@ -782,6 +774,21 @@ FrozenSet LoadExclusions(string symbolPublishingExclusionsFile) } } + private TokenCredential GetTemporarySymbolCredential() + { + if (string.IsNullOrEmpty(TempSymbolsAzureDevOpsOrgToken)) + { + Log.LogMessage(MessageImportance.High, "Using DefaultIdentityTokenCredential for temporary symbol publishing because no TempSymbolsAzureDevOpsOrgToken was provided."); + return new DefaultIdentityTokenCredential( + new DefaultIdentityTokenCredentialOptions + { + ManagedIdentityClientId = ManagedIdentityClientId + }); + } + + return new PATCredential(TempSymbolsAzureDevOpsOrgToken); + } + /// /// Prep loose PDBs for publishing by downloading (V4 streaming only), and copying to a temporary directory. ///