From fd6a759a78dfa76d08168f3339c65b72433e132f Mon Sep 17 00:00:00 2001 From: Priyanka Tiwari Date: Tue, 10 Mar 2026 21:24:44 +0530 Subject: [PATCH 01/25] Convert Kerberos pipeline from Classic to YAML (11 jobs) --- .../templates/steps/kerberos-cleanup-step.yml | 45 ++ .../templates/steps/kerberos-init-step.yml | 109 ++++ eng/pipelines/sqlclient-kerberos.yml | 502 ++++++++++++++++++ 3 files changed, 656 insertions(+) create mode 100644 eng/pipelines/common/templates/steps/kerberos-cleanup-step.yml create mode 100644 eng/pipelines/common/templates/steps/kerberos-init-step.yml create mode 100644 eng/pipelines/sqlclient-kerberos.yml diff --git a/eng/pipelines/common/templates/steps/kerberos-cleanup-step.yml b/eng/pipelines/common/templates/steps/kerberos-cleanup-step.yml new file mode 100644 index 0000000000..98985f44c6 --- /dev/null +++ b/eng/pipelines/common/templates/steps/kerberos-cleanup-step.yml @@ -0,0 +1,45 @@ +################################################################################# +# Licensed to the .NET Foundation under one or more agreements. # +# The .NET Foundation licenses this file to you under the MIT license. # +# See the LICENSE file in the project root for more information. # +################################################################################# + +# This template leaves the Active Directory domain and destroys Kerberos +# credentials. It should be referenced at the end of any job that called +# kerberos-init-step.yml. +# +# All steps use condition: always() so that cleanup runs even when previous +# steps fail. + +parameters: + + # The Active Directory domain to leave (e.g. mydomain.contoso.com). + - name: kerberosDomain + type: string + + # The domain user account used during the join. + - name: kerberosDomainUser + type: string + + # The password for the domain user account. + - name: kerberosDomainPassword + type: string + +steps: + + - bash: | + set -uo pipefail + + DOMAIN="${{ parameters.kerberosDomain }}" + DOMAIN_USER="${{ parameters.kerberosDomainUser }}" + DOMAIN_PASSWORD="${{ parameters.kerberosDomainPassword }}" + DOMAIN_UPPER=$(echo "$DOMAIN" | tr '[:lower:]' '[:upper:]') + + # Leave the domain + echo "$DOMAIN_PASSWORD" | sudo realm leave "$DOMAIN_UPPER" --verbose \ + -U "$DOMAIN_USER@$DOMAIN_UPPER" || true + + # Destroy the TGT and credential cache + kdestroy || true + displayName: 'Clean up Kerberos (domain leave + kdestroy)' + condition: always() diff --git a/eng/pipelines/common/templates/steps/kerberos-init-step.yml b/eng/pipelines/common/templates/steps/kerberos-init-step.yml new file mode 100644 index 0000000000..d05a7727d0 --- /dev/null +++ b/eng/pipelines/common/templates/steps/kerberos-init-step.yml @@ -0,0 +1,109 @@ +################################################################################# +# Licensed to the .NET Foundation under one or more agreements. # +# The .NET Foundation licenses this file to you under the MIT license. # +# See the LICENSE file in the project root for more information. # +################################################################################# + +# This template joins a Linux agent to an Active Directory domain using Kerberos +# and acquires a TGT (Ticket-Granting Ticket) for the specified domain user. +# +# Prerequisites: +# - The agent must be running on Ubuntu/Debian (uses apt-get). +# - The domain controller must be reachable from the agent network. +# +# After this step completes successfully, the agent will have: +# - Kerberos packages installed (krb5-user, realmd, sssd, adcli, etc.) +# - Hostname set to FQDN within the domain +# - NTP synchronized with the domain controller +# - Machine joined to the AD domain +# - A valid Kerberos TGT for the specified user + +parameters: + + # The Active Directory domain to join (e.g. mydomain.contoso.com). + - name: kerberosDomain + type: string + + # The Organizational Unit in which to place the computer account. + - name: kerberosDomainOU + type: string + + # The domain user account to authenticate with (sAMAccountName, without @realm). + - name: kerberosDomainUser + type: string + + # The password for the domain user account. + - name: kerberosDomainPassword + type: string + +steps: + + - bash: | + set -euo pipefail + + DOMAIN="${{ parameters.kerberosDomain }}" + DOMAIN_OU="${{ parameters.kerberosDomainOU }}" + DOMAIN_USER="${{ parameters.kerberosDomainUser }}" + DOMAIN_PASSWORD="${{ parameters.kerberosDomainPassword }}" + DOMAIN_UPPER=$(echo "$DOMAIN" | tr '[:lower:]' '[:upper:]') + + echo "Domain: $DOMAIN" + echo "Realm: $DOMAIN_UPPER" + echo "User: $DOMAIN_USER" + echo "OU: $DOMAIN_OU" + + if [ -z "$DOMAIN_PASSWORD" ]; then + echo "##vso[task.logissue type=error]KerberosDomainPassword is empty" + exit 1 + fi + + # ----------------------------------------------------------------------- + # Install Kerberos and AD integration packages + # ----------------------------------------------------------------------- + echo 'debconf debconf/frontend select Noninteractive' | sudo debconf-set-selections + + sudo apt-get -y update + sudo apt-get install -y dialog apt-utils + sudo apt-get install -y \ + krb5-user samba sssd sssd-tools libnss-sss libpam-sss \ + ntp ntpdate realmd adcli + + # ----------------------------------------------------------------------- + # Set the hostname to FQDN within the domain + # ----------------------------------------------------------------------- + sudo hostnamectl set-hostname "$(hostname).$DOMAIN" + + # ----------------------------------------------------------------------- + # Synchronize time with the domain controller (required for Kerberos) + # ----------------------------------------------------------------------- + echo "server $DOMAIN" | sudo tee -a /etc/ntp.conf + sudo systemctl stop ntp + sudo ntpdate "$DOMAIN" + sudo systemctl start ntp + + # ----------------------------------------------------------------------- + # Configure Kerberos realm + # ----------------------------------------------------------------------- + echo "[libdefaults] + default_realm = $DOMAIN_UPPER + rdns = false" | sudo tee /etc/krb5.conf + + # ----------------------------------------------------------------------- + # Discover and join the domain + # ----------------------------------------------------------------------- + sudo realm discover "$DOMAIN_UPPER" + + echo "$DOMAIN_PASSWORD" | sudo realm join --verbose "$DOMAIN_UPPER" \ + -U "$DOMAIN_USER@$DOMAIN_UPPER" \ + --computer-ou "OU=$DOMAIN_OU" + + realm list + + # ----------------------------------------------------------------------- + # Acquire a Kerberos TGT + # ----------------------------------------------------------------------- + echo "$DOMAIN_PASSWORD" | kinit "$DOMAIN_USER@$DOMAIN_UPPER" + + klist + sudo ifconfig + displayName: 'Initialize Kerberos (domain join + kinit)' diff --git a/eng/pipelines/sqlclient-kerberos.yml b/eng/pipelines/sqlclient-kerberos.yml new file mode 100644 index 0000000000..c8e22f3b59 --- /dev/null +++ b/eng/pipelines/sqlclient-kerberos.yml @@ -0,0 +1,502 @@ +################################################################################# +# Licensed to the .NET Foundation under one or more agreements. # +# The .NET Foundation licenses this file to you under the MIT license. # +# See the LICENSE file in the project root for more information. # +################################################################################# + +# ============================================================================= +# sqlclient-kerberos +# ============================================================================= +# Nightly Kerberos authentication test pipeline for Microsoft.Data.SqlClient. +# Replaces the Classic "Test-SqlClient-Kerberos-Azure" pipeline. +# +# Job breakdown (11 total): +# Stage: Windows-netfx → 1 job (net462) +# Stage: Windows-netcore → 6 jobs (3 TFs × 2 ManagedSNI) +# Stage: Linux-netcore → 3 jobs (3 TFs, ManagedSNI=True) +# Stage: Merge-Code-Coverage → 1 job +# +# Required ADO variable groups (link in pipeline UI): +# - kv-sqldrivers-shared +# - ADO - SQL Server Setup variables +# +# Required ADO pipeline variables (define in pipeline UI): +# - REMOTE_TCP_CONN_STRING (connection string for remote SQL via TCP) +# - REMOTE_NP_CONN_STRING (connection string for remote SQL via Named Pipes) +# - KerberosDomain (AD domain, e.g. mydomain.contoso.com) +# - KerberosDomainOU (OU for computer accounts) +# - KerberosDomainUser (domain user, sAMAccountName) +# - KerberosDomainPassword (secret — domain user password) +# ============================================================================= + +trigger: none # Nightly only — no CI trigger +pr: none # Not triggered by PRs + +schedules: + - cron: '0 7 * * 1,2,3,4,5' + displayName: Weekday nightly run (07:00 UTC) + branches: + include: + - main + - release/6.1 + - release/6.0 + always: true + +name: $(date:yyyyMMdd)$(rev:.r) + +variables: + - name: Configuration + value: Release + - name: Platform + value: AnyCPU + +# ============================================================================= +# STAGES +# ============================================================================= +stages: + + # =========================================================================== + # Stage 1 — Windows · .NET Framework (1 job) + # =========================================================================== + - stage: Windows_netfx + displayName: Windows - .NET Framework + dependsOn: [] + jobs: + - job: Windows_netfx_net462 + displayName: Windows netfx net462 + timeoutInMinutes: 360 + pool: + name: ADO-Trusted-Domain-Win-WestUS2 + demands: + - ImageOverride -equals ADO-MMS22-SQL19 + steps: + + - checkout: self + clean: true + fetchDepth: 1 + fetchTags: false + + - task: NuGetToolInstaller@1 + displayName: Use NuGet + + # Install .NET SDK (from global.json) and runtimes using the shared + # template. All three runtimes are installed so the build system can + # resolve any needed references. + - template: /eng/pipelines/steps/install-dotnet.yml@self + parameters: + runtimes: ['8.x', '9.x', '10.x'] + + # --- Update test configuration --- + - pwsh: | + $jdata = Get-Content -Raw "config.default.json" | ConvertFrom-Json + foreach ($p in $jdata) { + $p.TCPConnectionString = "$(REMOTE_TCP_CONN_STRING)" + $p.NPConnectionString = "$(REMOTE_NP_CONN_STRING)" + $p.SupportsIntegratedSecurity = "true" + $p.UseManagedSNIOnWindows = "false" + } + $jdata | ConvertTo-Json | Set-Content "config.json" + workingDirectory: src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities + displayName: Update test config.json + + # --- Prepare Windows services --- + - powershell: | + $svc = Get-Service -Name SQLBrowser -ErrorAction SilentlyContinue + if ($null -ne $svc) { + Set-Service -StartupType Automatic SQLBrowser + if ($svc.Status -ne 'Running') { Start-Service SQLBrowser } + Get-Service SQLBrowser | Select-Object Name, StartType, Status + } + displayName: Start SQL Server Browser + + - powershell: | + Set-DtcNetworkSetting -DtcName "Local" ` + -InboundTransactionsEnabled $true ` + -OutboundTransactionsEnabled $true ` + -RemoteClientAccessEnabled $true ` + -Confirm:$false + + Get-NetFirewallRule -DisplayName "Distributed Transaction Coordinator (RPC)" | Set-NetFirewallRule -Profile Domain -Action Allow -Enabled True + Get-NetFirewallRule -DisplayName "Distributed Transaction Coordinator (RPC-EPMAP)" | Set-NetFirewallRule -Profile Domain -Action Allow -Enabled True + Get-NetFirewallRule -DisplayName "Distributed Transaction Coordinator (TCP-Out)" | Set-NetFirewallRule -Profile Domain -Action Allow -Enabled True + Get-NetFirewallRule -DisplayName "Distributed Transaction Coordinator (TCP-In)" | Set-NetFirewallRule -Profile Domain -Action Allow -Enabled True + displayName: Enable Network DTC Access + + # --- Build --- + - task: MSBuild@1 + displayName: Restore NuGets + inputs: + solution: build.proj + msbuildArchitecture: x64 + msbuildArguments: /t:restore + + - task: MSBuild@1 + displayName: Build Driver + retryCountOnTaskFailure: 1 + inputs: + solution: build.proj + msbuildArchitecture: x64 + platform: $(Platform) + configuration: $(Configuration) + msbuildArguments: -t:BuildAll + + - task: MSBuild@1 + displayName: Build Tests (net462) + inputs: + solution: build.proj + platform: $(Platform) + configuration: $(Configuration) + msbuildArguments: -t:BuildTestsNetFx -p:TF=net462 + + - pwsh: dotnet sdk check + displayName: .NET SDK check + condition: succeededOrFailed() + + # --- Run tests --- + - task: MSBuild@1 + displayName: Run Tests (net462) + retryCountOnTaskFailure: 2 + inputs: + solution: build.proj + msbuildArchitecture: x64 + platform: $(Platform) + configuration: $(Configuration) + msbuildArguments: -t:RunTests -p:TF=net462 + + # --- Publish results & coverage --- + - task: PublishTestResults@2 + displayName: Publish Test Results + condition: succeededOrFailed() + inputs: + testResultsFormat: VSTest + testResultsFiles: | + TestResults/*.trx + TestResults/**/*.coverage + mergeTestResults: true + testRunTitle: Windows-netfx-net462 + buildPlatform: $(Platform) + buildConfiguration: $(Configuration) + + - pwsh: | + cd TestResults + Get-ChildItem -Filter "*.coverage" -Recurse | + Rename-Item -NewName { "net462" + $_.Name } + displayName: Rename coverage files + condition: succeededOrFailed() + + - task: PublishPipelineArtifact@1 + displayName: Publish Test Artifacts + condition: succeededOrFailed() + inputs: + targetPath: TestResults + artifact: net462-$(System.JobId) + + # =========================================================================== + # Stage 2 — Windows · .NET Core (6 jobs = 3 TFs × 2 ManagedSNI) + # =========================================================================== + - stage: Windows_netcore + displayName: Windows - .NET Core + dependsOn: [] + jobs: + - job: Windows_netcore + displayName: Windows netcore + timeoutInMinutes: 360 + strategy: + matrix: + net8_NativeSNI: + targetFramework: net8.0 + managedSNI: 'false' + net8_ManagedSNI: + targetFramework: net8.0 + managedSNI: 'true' + net9_NativeSNI: + targetFramework: net9.0 + managedSNI: 'false' + net9_ManagedSNI: + targetFramework: net9.0 + managedSNI: 'true' + net10_NativeSNI: + targetFramework: net10.0 + managedSNI: 'false' + net10_ManagedSNI: + targetFramework: net10.0 + managedSNI: 'true' + pool: + name: ADO-Trusted-Domain-Win-WestUS2 + demands: + - ImageOverride -equals ADO-MMS22-SQL19 + steps: + + - checkout: self + clean: true + fetchDepth: 1 + fetchTags: false + + - task: NuGetToolInstaller@1 + displayName: Use NuGet + + - template: /eng/pipelines/steps/install-dotnet.yml@self + parameters: + runtimes: ['8.x', '9.x', '10.x'] + + # --- Update test configuration --- + # Uses runtime variables from the matrix ($(managedSNI)) so we use + # inline PowerShell instead of the shared config template which + # requires compile-time parameters. + - pwsh: | + $jdata = Get-Content -Raw "config.default.json" | ConvertFrom-Json + foreach ($p in $jdata) { + $p.TCPConnectionString = "$(REMOTE_TCP_CONN_STRING)" + $p.NPConnectionString = "$(REMOTE_NP_CONN_STRING)" + $p.SupportsIntegratedSecurity = "true" + $p.UseManagedSNIOnWindows = "$(managedSNI)" + } + $jdata | ConvertTo-Json | Set-Content "config.json" + workingDirectory: src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities + displayName: Update test config.json + + # --- Prepare Windows services --- + - powershell: | + $svc = Get-Service -Name SQLBrowser -ErrorAction SilentlyContinue + if ($null -ne $svc) { + Set-Service -StartupType Automatic SQLBrowser + if ($svc.Status -ne 'Running') { Start-Service SQLBrowser } + Get-Service SQLBrowser | Select-Object Name, StartType, Status + } + displayName: Start SQL Server Browser + + - powershell: | + Set-DtcNetworkSetting -DtcName "Local" ` + -InboundTransactionsEnabled $true ` + -OutboundTransactionsEnabled $true ` + -RemoteClientAccessEnabled $true ` + -Confirm:$false + + Get-NetFirewallRule -DisplayName "Distributed Transaction Coordinator (RPC)" | Set-NetFirewallRule -Profile Domain -Action Allow -Enabled True + Get-NetFirewallRule -DisplayName "Distributed Transaction Coordinator (RPC-EPMAP)" | Set-NetFirewallRule -Profile Domain -Action Allow -Enabled True + Get-NetFirewallRule -DisplayName "Distributed Transaction Coordinator (TCP-Out)" | Set-NetFirewallRule -Profile Domain -Action Allow -Enabled True + Get-NetFirewallRule -DisplayName "Distributed Transaction Coordinator (TCP-In)" | Set-NetFirewallRule -Profile Domain -Action Allow -Enabled True + displayName: Enable Network DTC Access + + # --- Build --- + - task: MSBuild@1 + displayName: Restore NuGets + inputs: + solution: build.proj + msbuildArchitecture: x64 + msbuildArguments: /t:restore + + - task: MSBuild@1 + displayName: Build Driver + retryCountOnTaskFailure: 1 + inputs: + solution: build.proj + msbuildArchitecture: x64 + platform: $(Platform) + configuration: $(Configuration) + msbuildArguments: -t:BuildAll + + - task: MSBuild@1 + displayName: Build Tests ($(targetFramework)) + inputs: + solution: build.proj + platform: $(Platform) + configuration: $(Configuration) + msbuildArguments: -t:BuildTestsNetCore -p:TF=$(targetFramework) + + - pwsh: dotnet sdk check + displayName: .NET SDK check + condition: succeededOrFailed() + + # --- Run tests --- + - task: MSBuild@1 + displayName: Run Tests ($(targetFramework)) + retryCountOnTaskFailure: 2 + inputs: + solution: build.proj + msbuildArchitecture: x64 + platform: $(Platform) + configuration: $(Configuration) + msbuildArguments: -t:RunTests -p:TF=$(targetFramework) + + # --- Publish results & coverage --- + - task: PublishTestResults@2 + displayName: Publish Test Results + condition: succeededOrFailed() + inputs: + testResultsFormat: VSTest + testResultsFiles: | + TestResults/*.trx + TestResults/**/*.coverage + mergeTestResults: true + testRunTitle: Windows-netcore-$(targetFramework)-ManagedSNI_$(managedSNI) + buildPlatform: $(Platform) + buildConfiguration: $(Configuration) + + - pwsh: | + cd TestResults + Get-ChildItem -Filter "*.coverage" -Recurse | + Rename-Item -NewName { "$(targetFramework)" + $_.Name } + displayName: Rename coverage files + condition: succeededOrFailed() + + - task: PublishPipelineArtifact@1 + displayName: Publish Test Artifacts + condition: succeededOrFailed() + inputs: + targetPath: TestResults + artifact: $(targetFramework)-ManagedSNI_$(managedSNI)-$(System.JobId) + + # =========================================================================== + # Stage 3 — Linux · .NET Core + Kerberos (3 jobs = 3 TFs) + # =========================================================================== + - stage: Linux_netcore + displayName: Linux - .NET Core (Kerberos) + dependsOn: [] + jobs: + - job: Linux_netcore + displayName: Linux Kerberos + timeoutInMinutes: 360 + strategy: + matrix: + net8: + targetFramework: net8.0 + net9: + targetFramework: net9.0 + net10: + targetFramework: net10.0 + pool: + name: ADO-Trusted-Linux-WestUS2 + demands: + - ImageOverride -equals ADO-UB20-SQL22 + steps: + + - checkout: self + clean: true + fetchDepth: 1 + fetchTags: false + + - task: NuGetToolInstaller@1 + displayName: Use NuGet + + # --- Update test configuration (with Kerberos credentials) --- + - pwsh: | + $jdata = Get-Content -Raw "config.default.json" | ConvertFrom-Json + foreach ($p in $jdata) { + $p.TCPConnectionString = "$(REMOTE_TCP_CONN_STRING)" + $p.NPConnectionString = "$(REMOTE_NP_CONN_STRING)" + $p.SupportsIntegratedSecurity = "true" + } + $jdata | Add-Member -NotePropertyName "KerberosDomainUser" -NotePropertyValue "$(KerberosDomainUser)" -Force + $jdata | Add-Member -NotePropertyName "KerberosDomainPassword" -NotePropertyValue "$(KerberosDomainPassword)" -Force + $jdata | ConvertTo-Json | Set-Content "config.json" + workingDirectory: src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities + displayName: Update test config.json (Kerberos) + + # --- Kerberos domain join --- + - template: /eng/pipelines/common/templates/steps/kerberos-init-step.yml@self + parameters: + kerberosDomain: $(KerberosDomain) + kerberosDomainOU: $(KerberosDomainOU) + kerberosDomainUser: $(KerberosDomainUser) + kerberosDomainPassword: $(KerberosDomainPassword) + + # --- Verify SQL connectivity --- + - pwsh: | + Install-Module -Name SqlServer -Force -Confirm:$false + Import-Module SqlServer + Invoke-Sqlcmd -Query "SELECT @@VERSION, @@SERVERNAME" -ConnectionString "$(REMOTE_TCP_CONN_STRING)" + displayName: Verify SQL connectivity + + # --- Install .NET SDK and runtimes --- + - template: /eng/pipelines/steps/install-dotnet.yml@self + parameters: + runtimes: ['8.x', '9.x', '10.x'] + + # --- Build --- + - task: DotNetCoreCLI@2 + displayName: Build Driver + inputs: + command: custom + projects: build.proj + custom: msbuild + arguments: >- + -t:BuildAll + -p:TestEnabled=true + -p:GenerateDocumentationFile=false + -p:Configuration=$(Configuration) + -p:Platform=$(Platform) + + - task: DotNetCoreCLI@2 + displayName: Build Tests ($(targetFramework)) + inputs: + command: custom + projects: build.proj + custom: msbuild + arguments: >- + -t:BuildTestsNetCore + -p:Configuration=$(Configuration) + -p:OSGroup=Unix + -p:TargetNetCoreVersion=$(targetFramework) + + # --- Run tests --- + - task: DotNetCoreCLI@2 + displayName: Run Tests ($(targetFramework)) + inputs: + command: custom + projects: build.proj + custom: msbuild + arguments: >- + -t:RunTests + -p:TF=$(targetFramework) + -p:Configuration=$(Configuration) + + # --- Publish results & coverage --- + - task: PublishTestResults@2 + displayName: Publish Test Results + condition: succeededOrFailed() + inputs: + testResultsFormat: VSTest + testResultsFiles: | + TestResults/*.trx + TestResults/**/*.coverage + mergeTestResults: true + testRunTitle: Linux-netcore-$(targetFramework) + buildPlatform: $(Platform) + buildConfiguration: $(Configuration) + + - pwsh: | + cd TestResults + Get-ChildItem -Filter "*.coverage" -Recurse | + Rename-Item -NewName { "$(targetFramework)" + $_.Name } + displayName: Rename coverage files + condition: succeededOrFailed() + + - task: PublishPipelineArtifact@1 + displayName: Publish Test Artifacts + condition: succeededOrFailed() + inputs: + targetPath: TestResults + artifact: $(targetFramework)-linux-$(System.JobId) + + # --- Kerberos cleanup (always runs) --- + - template: /eng/pipelines/common/templates/steps/kerberos-cleanup-step.yml@self + parameters: + kerberosDomain: $(KerberosDomain) + kerberosDomainUser: $(KerberosDomainUser) + kerberosDomainPassword: $(KerberosDomainPassword) + + # =========================================================================== + # Stage 4 — Merge Code Coverage (1 job) + # =========================================================================== + - stage: Merge_Code_Coverage + displayName: Merge Code Coverage + dependsOn: + - Windows_netfx + - Windows_netcore + - Linux_netcore + condition: succeededOrFailed() + jobs: + - template: /eng/pipelines/common/templates/jobs/ci-code-coverage-job.yml@self + parameters: + upload: false From 44d6f2253d97bc1960e34d09c872c5b1bbac36fb Mon Sep 17 00:00:00 2001 From: Priyanka Tiwari Date: Mon, 16 Mar 2026 13:21:26 +0530 Subject: [PATCH 02/25] fix: use env mapping for secret variables in pwsh inline scripts ADO does not macro-expand secret variables inside inline pwsh scripts. Use env: mapping to pass REMOTE_TCP_CONN_STRING, REMOTE_NP_CONN_STRING, KerberosDomainUser, KerberosDomainPassword, and managedSNI as environment variables, then access them via $env:VAR_NAME in PowerShell. --- eng/pipelines/sqlclient-kerberos.yml | 34 ++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/eng/pipelines/sqlclient-kerberos.yml b/eng/pipelines/sqlclient-kerberos.yml index c8e22f3b59..9359a22cfd 100644 --- a/eng/pipelines/sqlclient-kerberos.yml +++ b/eng/pipelines/sqlclient-kerberos.yml @@ -90,14 +90,17 @@ stages: - pwsh: | $jdata = Get-Content -Raw "config.default.json" | ConvertFrom-Json foreach ($p in $jdata) { - $p.TCPConnectionString = "$(REMOTE_TCP_CONN_STRING)" - $p.NPConnectionString = "$(REMOTE_NP_CONN_STRING)" + $p.TCPConnectionString = $env:REMOTE_TCP_CONN_STRING + $p.NPConnectionString = $env:REMOTE_NP_CONN_STRING $p.SupportsIntegratedSecurity = "true" $p.UseManagedSNIOnWindows = "false" } $jdata | ConvertTo-Json | Set-Content "config.json" workingDirectory: src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities displayName: Update test config.json + env: + REMOTE_TCP_CONN_STRING: $(REMOTE_TCP_CONN_STRING) + REMOTE_NP_CONN_STRING: $(REMOTE_NP_CONN_STRING) # --- Prepare Windows services --- - powershell: | @@ -246,14 +249,18 @@ stages: - pwsh: | $jdata = Get-Content -Raw "config.default.json" | ConvertFrom-Json foreach ($p in $jdata) { - $p.TCPConnectionString = "$(REMOTE_TCP_CONN_STRING)" - $p.NPConnectionString = "$(REMOTE_NP_CONN_STRING)" + $p.TCPConnectionString = $env:REMOTE_TCP_CONN_STRING + $p.NPConnectionString = $env:REMOTE_NP_CONN_STRING $p.SupportsIntegratedSecurity = "true" - $p.UseManagedSNIOnWindows = "$(managedSNI)" + $p.UseManagedSNIOnWindows = $env:MANAGED_SNI } $jdata | ConvertTo-Json | Set-Content "config.json" workingDirectory: src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities displayName: Update test config.json + env: + REMOTE_TCP_CONN_STRING: $(REMOTE_TCP_CONN_STRING) + REMOTE_NP_CONN_STRING: $(REMOTE_NP_CONN_STRING) + MANAGED_SNI: $(managedSNI) # --- Prepare Windows services --- - powershell: | @@ -383,15 +390,20 @@ stages: - pwsh: | $jdata = Get-Content -Raw "config.default.json" | ConvertFrom-Json foreach ($p in $jdata) { - $p.TCPConnectionString = "$(REMOTE_TCP_CONN_STRING)" - $p.NPConnectionString = "$(REMOTE_NP_CONN_STRING)" + $p.TCPConnectionString = $env:REMOTE_TCP_CONN_STRING + $p.NPConnectionString = $env:REMOTE_NP_CONN_STRING $p.SupportsIntegratedSecurity = "true" } - $jdata | Add-Member -NotePropertyName "KerberosDomainUser" -NotePropertyValue "$(KerberosDomainUser)" -Force - $jdata | Add-Member -NotePropertyName "KerberosDomainPassword" -NotePropertyValue "$(KerberosDomainPassword)" -Force + $jdata | Add-Member -NotePropertyName "KerberosDomainUser" -NotePropertyValue $env:KERBEROS_DOMAIN_USER -Force + $jdata | Add-Member -NotePropertyName "KerberosDomainPassword" -NotePropertyValue $env:KERBEROS_DOMAIN_PASSWORD -Force $jdata | ConvertTo-Json | Set-Content "config.json" workingDirectory: src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities displayName: Update test config.json (Kerberos) + env: + REMOTE_TCP_CONN_STRING: $(REMOTE_TCP_CONN_STRING) + REMOTE_NP_CONN_STRING: $(REMOTE_NP_CONN_STRING) + KERBEROS_DOMAIN_USER: $(KerberosDomainUser) + KERBEROS_DOMAIN_PASSWORD: $(KerberosDomainPassword) # --- Kerberos domain join --- - template: /eng/pipelines/common/templates/steps/kerberos-init-step.yml@self @@ -405,8 +417,10 @@ stages: - pwsh: | Install-Module -Name SqlServer -Force -Confirm:$false Import-Module SqlServer - Invoke-Sqlcmd -Query "SELECT @@VERSION, @@SERVERNAME" -ConnectionString "$(REMOTE_TCP_CONN_STRING)" + Invoke-Sqlcmd -Query "SELECT @@VERSION, @@SERVERNAME" -ConnectionString $env:REMOTE_TCP_CONN_STRING displayName: Verify SQL connectivity + env: + REMOTE_TCP_CONN_STRING: $(REMOTE_TCP_CONN_STRING) # --- Install .NET SDK and runtimes --- - template: /eng/pipelines/steps/install-dotnet.yml@self From e23436002a737ee096ec41a04b19a4c34744ecd7 Mon Sep 17 00:00:00 2001 From: Priyanka Tiwari Date: Mon, 16 Mar 2026 13:44:37 +0530 Subject: [PATCH 03/25] fix: use correct build.proj targets - BuildAll BuildAllConfigurations (Windows) - BuildAll BuildSqlClient (Linux) - Remove nonexistent BuildTestsNetFx/BuildTestsNetCore steps (tests build via RunTests) - Add -p:ReferenceType=Project for all build and test steps - Add -p:GenerateNuget=false to skip package generation - Use multi-line msbuildArguments for readability --- eng/pipelines/sqlclient-kerberos.yml | 67 +++++++++++++--------------- 1 file changed, 32 insertions(+), 35 deletions(-) diff --git a/eng/pipelines/sqlclient-kerberos.yml b/eng/pipelines/sqlclient-kerberos.yml index 9359a22cfd..79f8f716c6 100644 --- a/eng/pipelines/sqlclient-kerberos.yml +++ b/eng/pipelines/sqlclient-kerberos.yml @@ -131,7 +131,10 @@ stages: inputs: solution: build.proj msbuildArchitecture: x64 - msbuildArguments: /t:restore + msbuildArguments: >- + -t:restore + -p:ReferenceType=Project + retryCountOnTaskFailure: 1 - task: MSBuild@1 displayName: Build Driver @@ -141,15 +144,11 @@ stages: msbuildArchitecture: x64 platform: $(Platform) configuration: $(Configuration) - msbuildArguments: -t:BuildAll - - - task: MSBuild@1 - displayName: Build Tests (net462) - inputs: - solution: build.proj - platform: $(Platform) - configuration: $(Configuration) - msbuildArguments: -t:BuildTestsNetFx -p:TF=net462 + msbuildArguments: >- + -t:BuildAllConfigurations + -p:ReferenceType=Project + -p:GenerateNuget=false + -p:GenerateDocumentationFile=false - pwsh: dotnet sdk check displayName: .NET SDK check @@ -164,7 +163,10 @@ stages: msbuildArchitecture: x64 platform: $(Platform) configuration: $(Configuration) - msbuildArguments: -t:RunTests -p:TF=net462 + msbuildArguments: >- + -t:RunTests + -p:TF=net462 + -p:ReferenceType=Project # --- Publish results & coverage --- - task: PublishTestResults@2 @@ -291,7 +293,10 @@ stages: inputs: solution: build.proj msbuildArchitecture: x64 - msbuildArguments: /t:restore + msbuildArguments: >- + -t:restore + -p:ReferenceType=Project + retryCountOnTaskFailure: 1 - task: MSBuild@1 displayName: Build Driver @@ -301,15 +306,11 @@ stages: msbuildArchitecture: x64 platform: $(Platform) configuration: $(Configuration) - msbuildArguments: -t:BuildAll - - - task: MSBuild@1 - displayName: Build Tests ($(targetFramework)) - inputs: - solution: build.proj - platform: $(Platform) - configuration: $(Configuration) - msbuildArguments: -t:BuildTestsNetCore -p:TF=$(targetFramework) + msbuildArguments: >- + -t:BuildAllConfigurations + -p:ReferenceType=Project + -p:GenerateNuget=false + -p:GenerateDocumentationFile=false - pwsh: dotnet sdk check displayName: .NET SDK check @@ -324,7 +325,10 @@ stages: msbuildArchitecture: x64 platform: $(Platform) configuration: $(Configuration) - msbuildArguments: -t:RunTests -p:TF=$(targetFramework) + msbuildArguments: >- + -t:RunTests + -p:TF=$(targetFramework) + -p:ReferenceType=Project # --- Publish results & coverage --- - task: PublishTestResults@2 @@ -435,23 +439,14 @@ stages: projects: build.proj custom: msbuild arguments: >- - -t:BuildAll + -t:BuildSqlClient + -p:ReferenceType=Project -p:TestEnabled=true + -p:GenerateNuget=false -p:GenerateDocumentationFile=false -p:Configuration=$(Configuration) -p:Platform=$(Platform) - - - task: DotNetCoreCLI@2 - displayName: Build Tests ($(targetFramework)) - inputs: - command: custom - projects: build.proj - custom: msbuild - arguments: >- - -t:BuildTestsNetCore - -p:Configuration=$(Configuration) - -p:OSGroup=Unix - -p:TargetNetCoreVersion=$(targetFramework) + retryCountOnTaskFailure: 1 # --- Run tests --- - task: DotNetCoreCLI@2 @@ -463,7 +458,9 @@ stages: arguments: >- -t:RunTests -p:TF=$(targetFramework) + -p:ReferenceType=Project -p:Configuration=$(Configuration) + -p:Platform=$(Platform) # --- Publish results & coverage --- - task: PublishTestResults@2 From 4e5fefde7ab2ed77a7f7b724c39668db78eff8ec Mon Sep 17 00:00:00 2001 From: Priyanka Tiwari Date: Tue, 17 Mar 2026 16:22:41 +0530 Subject: [PATCH 04/25] fix: use BuildSqlClient instead of BuildAllConfigurations for Windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BuildAllConfigurations builds GenAPI tools and cross-OS packages which are not needed for testing and fail due to .NET 10 SDK incompatibility with VS 2022. BuildSqlClient is leaner — builds only the driver, which is all the test pipeline needs. --- eng/pipelines/sqlclient-kerberos.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eng/pipelines/sqlclient-kerberos.yml b/eng/pipelines/sqlclient-kerberos.yml index 79f8f716c6..2fe3f8c6b3 100644 --- a/eng/pipelines/sqlclient-kerberos.yml +++ b/eng/pipelines/sqlclient-kerberos.yml @@ -145,7 +145,7 @@ stages: platform: $(Platform) configuration: $(Configuration) msbuildArguments: >- - -t:BuildAllConfigurations + -t:BuildSqlClient -p:ReferenceType=Project -p:GenerateNuget=false -p:GenerateDocumentationFile=false @@ -307,7 +307,7 @@ stages: platform: $(Platform) configuration: $(Configuration) msbuildArguments: >- - -t:BuildAllConfigurations + -t:BuildSqlClient -p:ReferenceType=Project -p:GenerateNuget=false -p:GenerateDocumentationFile=false From d99a9d1657aebc8028a84022a4c9c595a15802dc Mon Sep 17 00:00:00 2001 From: Priyanka Tiwari Date: Tue, 17 Mar 2026 17:11:05 +0530 Subject: [PATCH 05/25] fix: use BuildNetFx for netfx, DotNetCoreCLI for netcore on Windows VS MSBuild (MSBuild@1) does not fully support .NET 10 SDK, causing NETSDK1005 errors when resolving P2P references across TFMs (net8.0 target for Microsoft.SqlServer.Server which only targets net46/netstandard2.0). - Windows netfx: use -t:BuildNetFx (only builds net462, avoids net8.0) - Windows netcore: switch to DotNetCoreCLI@2 (dotnet msbuild) which uses the .NET 10 SDK properly, matching the Linux build pattern - Remove separate Restore step for netcore (restore runs inside BuildSqlClient dependency chain) - Linux: unchanged (already uses DotNetCoreCLI@2) --- eng/pipelines/sqlclient-kerberos.yml | 45 ++++++++++++---------------- 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/eng/pipelines/sqlclient-kerberos.yml b/eng/pipelines/sqlclient-kerberos.yml index 2fe3f8c6b3..5c149b00c0 100644 --- a/eng/pipelines/sqlclient-kerberos.yml +++ b/eng/pipelines/sqlclient-kerberos.yml @@ -137,7 +137,7 @@ stages: retryCountOnTaskFailure: 1 - task: MSBuild@1 - displayName: Build Driver + displayName: Build Driver (netfx) retryCountOnTaskFailure: 1 inputs: solution: build.proj @@ -145,7 +145,7 @@ stages: platform: $(Platform) configuration: $(Configuration) msbuildArguments: >- - -t:BuildSqlClient + -t:BuildNetFx -p:ReferenceType=Project -p:GenerateNuget=false -p:GenerateDocumentationFile=false @@ -287,48 +287,41 @@ stages: Get-NetFirewallRule -DisplayName "Distributed Transaction Coordinator (TCP-In)" | Set-NetFirewallRule -Profile Domain -Action Allow -Enabled True displayName: Enable Network DTC Access - # --- Build --- - - task: MSBuild@1 - displayName: Restore NuGets - inputs: - solution: build.proj - msbuildArchitecture: x64 - msbuildArguments: >- - -t:restore - -p:ReferenceType=Project - retryCountOnTaskFailure: 1 - - - task: MSBuild@1 + # --- Build (using dotnet msbuild for .NET 10 SDK compat) --- + - task: DotNetCoreCLI@2 displayName: Build Driver retryCountOnTaskFailure: 1 inputs: - solution: build.proj - msbuildArchitecture: x64 - platform: $(Platform) - configuration: $(Configuration) - msbuildArguments: >- + command: custom + projects: build.proj + custom: msbuild + arguments: >- -t:BuildSqlClient -p:ReferenceType=Project + -p:TestEnabled=true -p:GenerateNuget=false -p:GenerateDocumentationFile=false + -p:Configuration=$(Configuration) + -p:Platform=$(Platform) - pwsh: dotnet sdk check displayName: .NET SDK check condition: succeededOrFailed() # --- Run tests --- - - task: MSBuild@1 + - task: DotNetCoreCLI@2 displayName: Run Tests ($(targetFramework)) - retryCountOnTaskFailure: 2 inputs: - solution: build.proj - msbuildArchitecture: x64 - platform: $(Platform) - configuration: $(Configuration) - msbuildArguments: >- + command: custom + projects: build.proj + custom: msbuild + arguments: >- -t:RunTests -p:TF=$(targetFramework) -p:ReferenceType=Project + -p:Configuration=$(Configuration) + -p:Platform=$(Platform) + retryCountOnTaskFailure: 2 # --- Publish results & coverage --- - task: PublishTestResults@2 From 5036d109c5bb8dded69c470b161343573d1cf298 Mon Sep 17 00:00:00 2001 From: Priyanka Tiwari Date: Tue, 17 Mar 2026 17:33:38 +0530 Subject: [PATCH 06/25] fix: add separate restore step for Windows netcore stage The BuildSqlClient target restores SqlServer.Server standalone for net46/netstandard2.0, but when the NetCore driver P2P references it, the .NET 10 SDK looks for net8.0 in the assets file and fails with NETSDK1005. Running a full -t:restore first (which restores all projects including P2P dependency chains) creates proper assets files before the build begins. This matches the CI pipeline pattern. --- eng/pipelines/sqlclient-kerberos.yml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/eng/pipelines/sqlclient-kerberos.yml b/eng/pipelines/sqlclient-kerberos.yml index 5c149b00c0..9f6f2b45ea 100644 --- a/eng/pipelines/sqlclient-kerberos.yml +++ b/eng/pipelines/sqlclient-kerberos.yml @@ -287,7 +287,19 @@ stages: Get-NetFirewallRule -DisplayName "Distributed Transaction Coordinator (TCP-In)" | Set-NetFirewallRule -Profile Domain -Action Allow -Enabled True displayName: Enable Network DTC Access - # --- Build (using dotnet msbuild for .NET 10 SDK compat) --- + # --- Restore & Build (using dotnet msbuild for .NET 10 SDK compat) --- + - task: DotNetCoreCLI@2 + displayName: Restore NuGets + retryCountOnTaskFailure: 1 + inputs: + command: custom + projects: build.proj + custom: msbuild + arguments: >- + -t:restore + -p:ReferenceType=Project + -p:Configuration=$(Configuration) + - task: DotNetCoreCLI@2 displayName: Build Driver retryCountOnTaskFailure: 1 From 0dc88aa165e5db3435648e33c7fa0ddfb4f88da8 Mon Sep 17 00:00:00 2001 From: Priyanka Tiwari Date: Tue, 17 Mar 2026 23:41:28 +0530 Subject: [PATCH 07/25] fix: add clean step to remove stale obj/artifacts before build BuildSqlClient succeeds locally but fails on ADO agents with NETSDK1005 because stale project.assets.json files from previous runs are cached in the agent workspace. Adding an explicit clean of obj/ and artifacts/ folders before restore ensures a fresh build environment. --- eng/pipelines/sqlclient-kerberos.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/eng/pipelines/sqlclient-kerberos.yml b/eng/pipelines/sqlclient-kerberos.yml index 9f6f2b45ea..ba928b60cd 100644 --- a/eng/pipelines/sqlclient-kerberos.yml +++ b/eng/pipelines/sqlclient-kerberos.yml @@ -287,6 +287,23 @@ stages: Get-NetFirewallRule -DisplayName "Distributed Transaction Coordinator (TCP-In)" | Set-NetFirewallRule -Profile Domain -Action Allow -Enabled True displayName: Enable Network DTC Access + # --- Clean stale obj folders to avoid NETSDK1005 from cached assets --- + - pwsh: | + $paths = @( + "src/Microsoft.SqlServer.Server/obj", + "src/Microsoft.Data.SqlClient/netcore/src/obj", + "src/Microsoft.Data.SqlClient/netfx/src/obj", + "src/Microsoft.Data.SqlClient/src/obj", + "artifacts" + ) + foreach ($p in $paths) { + if (Test-Path $p) { + Write-Host "Removing $p" + Remove-Item -Recurse -Force $p + } + } + displayName: Clean stale obj/artifacts folders + # --- Restore & Build (using dotnet msbuild for .NET 10 SDK compat) --- - task: DotNetCoreCLI@2 displayName: Restore NuGets From c3310e99619021077b5d5539bdc1889098a0d558 Mon Sep 17 00:00:00 2001 From: Priyanka Tiwari Date: Wed, 18 Mar 2026 14:20:10 +0530 Subject: [PATCH 08/25] fix: switch Windows netcore to MSBuild@1 + BuildAllConfigurations + BuildTools=false Match the CI pipeline pattern exactly: - Use MSBuild@1 (VS MSBuild) which is what the CI pipeline uses on Windows - Use BuildAllConfigurations target (what CI uses, builds all OS variants) - Add -p:BuildTools=false to skip GenAPI tools (avoids net10/VS2022 compat issue) - Remove clean step (not needed with correct build target) - BuildSqlClient with DotNetCoreCLI@2 fails on agents due to SqlServer.Server being resolved for net8.0 through P2P dependency chain, even though it only works locally --- eng/pipelines/sqlclient-kerberos.yml | 63 ++++++++++------------------ 1 file changed, 21 insertions(+), 42 deletions(-) diff --git a/eng/pipelines/sqlclient-kerberos.yml b/eng/pipelines/sqlclient-kerberos.yml index ba928b60cd..ca4cc35e62 100644 --- a/eng/pipelines/sqlclient-kerberos.yml +++ b/eng/pipelines/sqlclient-kerberos.yml @@ -287,70 +287,49 @@ stages: Get-NetFirewallRule -DisplayName "Distributed Transaction Coordinator (TCP-In)" | Set-NetFirewallRule -Profile Domain -Action Allow -Enabled True displayName: Enable Network DTC Access - # --- Clean stale obj folders to avoid NETSDK1005 from cached assets --- - - pwsh: | - $paths = @( - "src/Microsoft.SqlServer.Server/obj", - "src/Microsoft.Data.SqlClient/netcore/src/obj", - "src/Microsoft.Data.SqlClient/netfx/src/obj", - "src/Microsoft.Data.SqlClient/src/obj", - "artifacts" - ) - foreach ($p in $paths) { - if (Test-Path $p) { - Write-Host "Removing $p" - Remove-Item -Recurse -Force $p - } - } - displayName: Clean stale obj/artifacts folders - - # --- Restore & Build (using dotnet msbuild for .NET 10 SDK compat) --- - - task: DotNetCoreCLI@2 + # --- Restore & Build (matching CI pattern with MSBuild@1) --- + - task: MSBuild@1 displayName: Restore NuGets - retryCountOnTaskFailure: 1 inputs: - command: custom - projects: build.proj - custom: msbuild - arguments: >- + solution: build.proj + msbuildArchitecture: x64 + msbuildArguments: >- -t:restore -p:ReferenceType=Project - -p:Configuration=$(Configuration) + retryCountOnTaskFailure: 1 - - task: DotNetCoreCLI@2 + - task: MSBuild@1 displayName: Build Driver retryCountOnTaskFailure: 1 inputs: - command: custom - projects: build.proj - custom: msbuild - arguments: >- - -t:BuildSqlClient + solution: build.proj + msbuildArchitecture: x64 + platform: $(Platform) + configuration: $(Configuration) + msbuildArguments: >- + -t:BuildAllConfigurations -p:ReferenceType=Project - -p:TestEnabled=true + -p:BuildTools=false -p:GenerateNuget=false -p:GenerateDocumentationFile=false - -p:Configuration=$(Configuration) - -p:Platform=$(Platform) - pwsh: dotnet sdk check displayName: .NET SDK check condition: succeededOrFailed() # --- Run tests --- - - task: DotNetCoreCLI@2 + - task: MSBuild@1 displayName: Run Tests ($(targetFramework)) + retryCountOnTaskFailure: 2 inputs: - command: custom - projects: build.proj - custom: msbuild - arguments: >- + solution: build.proj + msbuildArchitecture: x64 + platform: $(Platform) + configuration: $(Configuration) + msbuildArguments: >- -t:RunTests -p:TF=$(targetFramework) -p:ReferenceType=Project - -p:Configuration=$(Configuration) - -p:Platform=$(Platform) - retryCountOnTaskFailure: 2 # --- Publish results & coverage --- - task: PublishTestResults@2 From f0eeebb2b73f83fedb82666c539eebc1be657bac Mon Sep 17 00:00:00 2001 From: Priyanka Tiwari Date: Wed, 18 Mar 2026 15:31:25 +0530 Subject: [PATCH 09/25] fix: use BuildNetCore target for Windows netcore stage BuildAllConfigurations uses BuildNetCoreAllOS which builds the driver for all OS variants (Unix, Windows_NT, AnyOS). This is unnecessary for a test pipeline and may contribute to the NETSDK1005 error where SqlServer.Server is incorrectly built for net8.0. BuildNetCore builds only for the host OS and is the appropriate target for test pipelines that just need the driver built locally. --- eng/pipelines/sqlclient-kerberos.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/eng/pipelines/sqlclient-kerberos.yml b/eng/pipelines/sqlclient-kerberos.yml index ca4cc35e62..3ea4b4c7ee 100644 --- a/eng/pipelines/sqlclient-kerberos.yml +++ b/eng/pipelines/sqlclient-kerberos.yml @@ -287,7 +287,7 @@ stages: Get-NetFirewallRule -DisplayName "Distributed Transaction Coordinator (TCP-In)" | Set-NetFirewallRule -Profile Domain -Action Allow -Enabled True displayName: Enable Network DTC Access - # --- Restore & Build (matching CI pattern with MSBuild@1) --- + # --- Restore & Build --- - task: MSBuild@1 displayName: Restore NuGets inputs: @@ -307,9 +307,8 @@ stages: platform: $(Platform) configuration: $(Configuration) msbuildArguments: >- - -t:BuildAllConfigurations + -t:BuildNetCore -p:ReferenceType=Project - -p:BuildTools=false -p:GenerateNuget=false -p:GenerateDocumentationFile=false From ed91fcd489bfef8193192b81c65f035fcf9cc35b Mon Sep 17 00:00:00 2001 From: Priyanka Tiwari Date: Wed, 18 Mar 2026 16:06:27 +0530 Subject: [PATCH 10/25] fix: bypass build.proj for Windows netcore build step Build the netcore driver csproj directly with 'dotnet build' via DotNetCoreCLI@2 instead of using build.proj targets (BuildNetCore, BuildAllConfigurations). build.proj's dependency chain includes BuildSqlServer which causes Microsoft.SqlServer.Server.csproj to be built with TargetFramework=net8.0 as a global property, triggering NETSDK1005 because its assets file only has targets for net46 and netstandard2.0. Building the driver csproj directly avoids this entirely: the driver uses a PackageReference to SqlServer.Server (version 1.0.0 from NuGet), not a ProjectReference. The driver's P2P dependencies (Abstractions, Logging) are automatically built via their ProjectReferences. The RunTests step continues to use build.proj since it just invokes 'dotnet test' via Exec, which uses the dotnet SDK for TFM resolution. --- eng/pipelines/sqlclient-kerberos.yml | 31 ++++++++++++---------------- 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/eng/pipelines/sqlclient-kerberos.yml b/eng/pipelines/sqlclient-kerberos.yml index 3ea4b4c7ee..86db9b0f89 100644 --- a/eng/pipelines/sqlclient-kerberos.yml +++ b/eng/pipelines/sqlclient-kerberos.yml @@ -288,28 +288,23 @@ stages: displayName: Enable Network DTC Access # --- Restore & Build --- - - task: MSBuild@1 - displayName: Restore NuGets - inputs: - solution: build.proj - msbuildArchitecture: x64 - msbuildArguments: >- - -t:restore - -p:ReferenceType=Project - retryCountOnTaskFailure: 1 - - - task: MSBuild@1 + # Build the driver directly with 'dotnet build' instead of going + # through build.proj targets. build.proj's BuildSqlServer target + # causes SqlServer.Server.csproj to be re-evaluated with the + # driver's TargetFramework (net8.0) as a global property, which + # triggers NETSDK1005 because its assets file only has targets + # for net46 and netstandard2.0. Building the driver csproj + # directly avoids this: SqlServer.Server is resolved as a NuGet + # PackageReference at version 1.0.0, not a ProjectReference. + - task: DotNetCoreCLI@2 displayName: Build Driver retryCountOnTaskFailure: 1 inputs: - solution: build.proj - msbuildArchitecture: x64 - platform: $(Platform) - configuration: $(Configuration) - msbuildArguments: >- - -t:BuildNetCore + command: build + projects: src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj + arguments: >- + -c $(Configuration) -p:ReferenceType=Project - -p:GenerateNuget=false -p:GenerateDocumentationFile=false - pwsh: dotnet sdk check From 2952136b765cc251efbad533287626bf530826c3 Mon Sep 17 00:00:00 2001 From: Priyanka Tiwari Date: Wed, 18 Mar 2026 16:25:34 +0530 Subject: [PATCH 11/25] fix: clean stale obj dirs + explicit restore for Windows netcore Root cause: self-hosted ADO agents preserve workspace between runs. Stale project.assets.json files from prior runs (which may have been restored via build.proj with different global properties) cause NETSDK1005 when the build tries to use them. Changes: - Add workspace.clean=all at the job level for full workspace purge - Explicitly delete obj/ dirs for all P2P dependencies before build - Separate restore step with 'dotnet restore' (proper TFM negotiation) - Build with --no-restore to prevent implicit restore from skipping the fresh restore output --- eng/pipelines/sqlclient-kerberos.yml | 44 ++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/eng/pipelines/sqlclient-kerberos.yml b/eng/pipelines/sqlclient-kerberos.yml index 86db9b0f89..fb976abb80 100644 --- a/eng/pipelines/sqlclient-kerberos.yml +++ b/eng/pipelines/sqlclient-kerberos.yml @@ -206,6 +206,8 @@ stages: - job: Windows_netcore displayName: Windows netcore timeoutInMinutes: 360 + workspace: + clean: all # Purge obj/artifacts from prior runs on self-hosted agents strategy: matrix: net8_NativeSNI: @@ -287,15 +289,38 @@ stages: Get-NetFirewallRule -DisplayName "Distributed Transaction Coordinator (TCP-In)" | Set-NetFirewallRule -Profile Domain -Action Allow -Enabled True displayName: Enable Network DTC Access - # --- Restore & Build --- - # Build the driver directly with 'dotnet build' instead of going - # through build.proj targets. build.proj's BuildSqlServer target - # causes SqlServer.Server.csproj to be re-evaluated with the - # driver's TargetFramework (net8.0) as a global property, which - # triggers NETSDK1005 because its assets file only has targets - # for net46 and netstandard2.0. Building the driver csproj - # directly avoids this: SqlServer.Server is resolved as a NuGet - # PackageReference at version 1.0.0, not a ProjectReference. + # --- Clean & Restore & Build --- + # Remove stale obj/ directories from prior builds on self-hosted + # agents. Cached project.assets.json files that were restored for + # a different set of global properties (e.g. from a build.proj run + # that set TargetFramework=net8.0 on netstandard2.0 projects) cause + # NETSDK1005. A fresh restore fixes this. + - pwsh: | + Write-Host "Cleaning stale obj/ and artifacts/ directories..." + @( + "src/Microsoft.SqlServer.Server/obj", + "src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/obj", + "src/Microsoft.Data.SqlClient.Extensions/Logging/src/obj", + "src/Microsoft.Data.SqlClient/netcore/src/obj", + "src/Microsoft.Data.SqlClient/netcore/ref/obj", + "artifacts" + ) | ForEach-Object { + if (Test-Path $_) { + Remove-Item -Recurse -Force $_ + Write-Host " Removed $_" + } + } + displayName: Clean stale obj directories + + - task: DotNetCoreCLI@2 + displayName: Restore NuGets + inputs: + command: restore + projects: src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj + arguments: >- + -p:ReferenceType=Project + retryCountOnTaskFailure: 1 + - task: DotNetCoreCLI@2 displayName: Build Driver retryCountOnTaskFailure: 1 @@ -303,6 +328,7 @@ stages: command: build projects: src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj arguments: >- + --no-restore -c $(Configuration) -p:ReferenceType=Project -p:GenerateDocumentationFile=false From 41c3ab777168f2b8f52538276f624bd8736b2f60 Mon Sep 17 00:00:00 2001 From: Priyanka Tiwari Date: Wed, 18 Mar 2026 17:16:19 +0530 Subject: [PATCH 12/25] fix: workaround P2P TFM negotiation failure on ADO agents The .NET 10 SDK on self-hosted ADO agents fails to negotiate TFMs for ProjectReferences from net8.0/net9.0 projects to netstandard2.0 projects. Instead of negotiating net8.0 -> netstandard2.0, MSBuild passes TargetFramework=net8.0 directly, causing NETSDK1005 because the assets file only has targets for netstandard2.0. The workaround creates a temporary Directory.Build.targets that sets SetTargetFramework metadata on Extensions P2P references. When SetTargetFramework is set, MSBuild skips the GetTargetFrameworks negotiation protocol and uses the specified TFM directly. The file is removed before the test step to avoid affecting test builds. --- eng/pipelines/sqlclient-kerberos.yml | 60 ++++++++++++++-------------- 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/eng/pipelines/sqlclient-kerberos.yml b/eng/pipelines/sqlclient-kerberos.yml index fb976abb80..4aab092568 100644 --- a/eng/pipelines/sqlclient-kerberos.yml +++ b/eng/pipelines/sqlclient-kerberos.yml @@ -289,37 +289,28 @@ stages: Get-NetFirewallRule -DisplayName "Distributed Transaction Coordinator (TCP-In)" | Set-NetFirewallRule -Profile Domain -Action Allow -Enabled True displayName: Enable Network DTC Access - # --- Clean & Restore & Build --- - # Remove stale obj/ directories from prior builds on self-hosted - # agents. Cached project.assets.json files that were restored for - # a different set of global properties (e.g. from a build.proj run - # that set TargetFramework=net8.0 on netstandard2.0 projects) cause - # NETSDK1005. A fresh restore fixes this. + # --- Build --- + # Workaround: .NET 10 SDK on ADO agents fails to negotiate TFMs + # for P2P references to netstandard2.0 projects. Instead of + # negotiating net8.0 → netstandard2.0, MSBuild passes net8.0 + # directly, causing NETSDK1005. We create a temporary + # Directory.Build.targets that explicitly sets the TFM for + # Extensions P2P references, bypassing the broken negotiation. - pwsh: | - Write-Host "Cleaning stale obj/ and artifacts/ directories..." - @( - "src/Microsoft.SqlServer.Server/obj", - "src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/obj", - "src/Microsoft.Data.SqlClient.Extensions/Logging/src/obj", - "src/Microsoft.Data.SqlClient/netcore/src/obj", - "src/Microsoft.Data.SqlClient/netcore/ref/obj", - "artifacts" - ) | ForEach-Object { - if (Test-Path $_) { - Remove-Item -Recurse -Force $_ - Write-Host " Removed $_" - } - } - displayName: Clean stale obj directories - - - task: DotNetCoreCLI@2 - displayName: Restore NuGets - inputs: - command: restore - projects: src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj - arguments: >- - -p:ReferenceType=Project - retryCountOnTaskFailure: 1 + @' + + + + + TargetFramework=netstandard2.0 + + + + + '@ | Set-Content -Path "Directory.Build.targets" -Encoding UTF8 + Write-Host "Created Directory.Build.targets with P2P TFM fix" + Get-Content "Directory.Build.targets" + displayName: Fix P2P TFM negotiation for Extensions - task: DotNetCoreCLI@2 displayName: Build Driver @@ -328,7 +319,6 @@ stages: command: build projects: src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj arguments: >- - --no-restore -c $(Configuration) -p:ReferenceType=Project -p:GenerateDocumentationFile=false @@ -337,6 +327,14 @@ stages: displayName: .NET SDK check condition: succeededOrFailed() + # Remove temporary targets file before test step + - pwsh: | + if (Test-Path "Directory.Build.targets") { + Remove-Item "Directory.Build.targets" + Write-Host "Removed temporary Directory.Build.targets" + } + displayName: Remove temporary Directory.Build.targets + # --- Run tests --- - task: MSBuild@1 displayName: Run Tests ($(targetFramework)) From c27e6d2fe6f0045894c1620e1a483fc6e02fe532 Mon Sep 17 00:00:00 2001 From: priyankatiwari08 Date: Thu, 9 Apr 2026 14:15:25 +0530 Subject: [PATCH 13/25] fix: Replace DotNetCoreCLI@2 csproj build with MSBuild@1 build.proj -t:BuildNetCore The .NET 10 SDK fails to negotiate TFMs for P2P references to netstandard2.0 projects (Abstractions, Logging) when building the .csproj directly, causing NETSDK1005. Using build.proj -t:BuildNetCore routes through the standard build targets which handle dependencies correctly, matching the approach used in the Windows-netfx stage. --- eng/pipelines/sqlclient-kerberos.yml | 52 +++++++++------------------- 1 file changed, 17 insertions(+), 35 deletions(-) diff --git a/eng/pipelines/sqlclient-kerberos.yml b/eng/pipelines/sqlclient-kerberos.yml index 4aab092568..1eff3c8a6b 100644 --- a/eng/pipelines/sqlclient-kerberos.yml +++ b/eng/pipelines/sqlclient-kerberos.yml @@ -290,36 +290,26 @@ stages: displayName: Enable Network DTC Access # --- Build --- - # Workaround: .NET 10 SDK on ADO agents fails to negotiate TFMs - # for P2P references to netstandard2.0 projects. Instead of - # negotiating net8.0 → netstandard2.0, MSBuild passes net8.0 - # directly, causing NETSDK1005. We create a temporary - # Directory.Build.targets that explicitly sets the TFM for - # Extensions P2P references, bypassing the broken negotiation. - - pwsh: | - @' - - - - - TargetFramework=netstandard2.0 - - - - - '@ | Set-Content -Path "Directory.Build.targets" -Encoding UTF8 - Write-Host "Created Directory.Build.targets with P2P TFM fix" - Get-Content "Directory.Build.targets" - displayName: Fix P2P TFM negotiation for Extensions + - task: MSBuild@1 + displayName: Restore NuGets + retryCountOnTaskFailure: 1 + inputs: + solution: build.proj + msbuildArchitecture: x64 + msbuildArguments: >- + -t:restore + -p:ReferenceType=Project - - task: DotNetCoreCLI@2 - displayName: Build Driver + - task: MSBuild@1 + displayName: Build Driver (netcore) retryCountOnTaskFailure: 1 inputs: - command: build - projects: src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj - arguments: >- - -c $(Configuration) + solution: build.proj + msbuildArchitecture: x64 + platform: $(Platform) + configuration: $(Configuration) + msbuildArguments: >- + -t:BuildNetCore -p:ReferenceType=Project -p:GenerateDocumentationFile=false @@ -327,14 +317,6 @@ stages: displayName: .NET SDK check condition: succeededOrFailed() - # Remove temporary targets file before test step - - pwsh: | - if (Test-Path "Directory.Build.targets") { - Remove-Item "Directory.Build.targets" - Write-Host "Removed temporary Directory.Build.targets" - } - displayName: Remove temporary Directory.Build.targets - # --- Run tests --- - task: MSBuild@1 displayName: Run Tests ($(targetFramework)) From fa16c85eb236e1b09a2d74690045ac0c76b57281 Mon Sep 17 00:00:00 2001 From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com> Date: Thu, 30 Apr 2026 11:13:32 -0300 Subject: [PATCH 14/25] Updated to use the new build.proj targets and the .NET SDK exclusively. --- eng/pipelines/sqlclient-kerberos.yml | 165 ++++++++------------------- 1 file changed, 49 insertions(+), 116 deletions(-) diff --git a/eng/pipelines/sqlclient-kerberos.yml b/eng/pipelines/sqlclient-kerberos.yml index 1eff3c8a6b..9672de4d1e 100644 --- a/eng/pipelines/sqlclient-kerberos.yml +++ b/eng/pipelines/sqlclient-kerberos.yml @@ -33,23 +33,17 @@ trigger: none # Nightly only — no CI trigger pr: none # Not triggered by PRs schedules: - - cron: '0 7 * * 1,2,3,4,5' - displayName: Weekday nightly run (07:00 UTC) + - cron: '0 7 * * *' + displayName: Daily run (07:00 UTC) branches: include: - main - release/6.1 - - release/6.0 + - release/7.0 always: true name: $(date:yyyyMMdd)$(rev:.r) -variables: - - name: Configuration - value: Release - - name: Platform - value: AnyCPU - # ============================================================================= # STAGES # ============================================================================= @@ -76,15 +70,11 @@ stages: fetchDepth: 1 fetchTags: false - - task: NuGetToolInstaller@1 - displayName: Use NuGet - # Install .NET SDK (from global.json) and runtimes using the shared - # template. All three runtimes are installed so the build system can - # resolve any needed references. + # template. - template: /eng/pipelines/steps/install-dotnet.yml@self parameters: - runtimes: ['8.x', '9.x', '10.x'] + runtimes: ['8.x', '9.x'] # --- Update test configuration --- - pwsh: | @@ -126,47 +116,27 @@ stages: displayName: Enable Network DTC Access # --- Build --- - - task: MSBuild@1 - displayName: Restore NuGets - inputs: - solution: build.proj - msbuildArchitecture: x64 - msbuildArguments: >- - -t:restore - -p:ReferenceType=Project - retryCountOnTaskFailure: 1 - - - task: MSBuild@1 - displayName: Build Driver (netfx) + - task: DotNetCoreCLI@2 + displayName: Build SqlClient retryCountOnTaskFailure: 1 inputs: - solution: build.proj - msbuildArchitecture: x64 - platform: $(Platform) - configuration: $(Configuration) - msbuildArguments: >- - -t:BuildNetFx - -p:ReferenceType=Project - -p:GenerateNuget=false - -p:GenerateDocumentationFile=false - - - pwsh: dotnet sdk check - displayName: .NET SDK check - condition: succeededOrFailed() + command: build + projects: build.proj + arguments: >- + -t:BuildSqlClient + -p:Configuration=Release # --- Run tests --- - - task: MSBuild@1 + - task: DotNetCoreCLI@2 displayName: Run Tests (net462) retryCountOnTaskFailure: 2 inputs: - solution: build.proj - msbuildArchitecture: x64 - platform: $(Platform) - configuration: $(Configuration) - msbuildArguments: >- - -t:RunTests - -p:TF=net462 - -p:ReferenceType=Project + command: build + projects: build.proj + arguments: >- + -t:TestSqlClient + -p:TestFramework=net462 + -p:Configuration=Release # --- Publish results & coverage --- - task: PublishTestResults@2 @@ -179,8 +149,7 @@ stages: TestResults/**/*.coverage mergeTestResults: true testRunTitle: Windows-netfx-net462 - buildPlatform: $(Platform) - buildConfiguration: $(Configuration) + buildConfiguration: Release - pwsh: | cd TestResults @@ -239,12 +208,9 @@ stages: fetchDepth: 1 fetchTags: false - - task: NuGetToolInstaller@1 - displayName: Use NuGet - - template: /eng/pipelines/steps/install-dotnet.yml@self parameters: - runtimes: ['8.x', '9.x', '10.x'] + runtimes: ['8.x', '9.x'] # --- Update test configuration --- # Uses runtime variables from the matrix ($(managedSNI)) so we use @@ -290,46 +256,28 @@ stages: displayName: Enable Network DTC Access # --- Build --- - - task: MSBuild@1 - displayName: Restore NuGets - retryCountOnTaskFailure: 1 - inputs: - solution: build.proj - msbuildArchitecture: x64 - msbuildArguments: >- - -t:restore - -p:ReferenceType=Project - - - task: MSBuild@1 - displayName: Build Driver (netcore) + + - task: DotNetCoreCLI@2 + displayName: Build SqlClient retryCountOnTaskFailure: 1 inputs: - solution: build.proj - msbuildArchitecture: x64 - platform: $(Platform) - configuration: $(Configuration) - msbuildArguments: >- - -t:BuildNetCore - -p:ReferenceType=Project - -p:GenerateDocumentationFile=false - - - pwsh: dotnet sdk check - displayName: .NET SDK check - condition: succeededOrFailed() + command: build + projects: build.proj + arguments: >- + -t:BuildSqlClient + -p:Configuration=Release # --- Run tests --- - - task: MSBuild@1 + - task: DotNetCoreCLI@2 displayName: Run Tests ($(targetFramework)) retryCountOnTaskFailure: 2 inputs: - solution: build.proj - msbuildArchitecture: x64 - platform: $(Platform) - configuration: $(Configuration) - msbuildArguments: >- - -t:RunTests - -p:TF=$(targetFramework) - -p:ReferenceType=Project + command: build + projects: build.proj + arguments: >- + -t:TestSqlClient + -p:TestFramework=$(targetFramework) + -p:Configuration=Release # --- Publish results & coverage --- - task: PublishTestResults@2 @@ -342,8 +290,7 @@ stages: TestResults/**/*.coverage mergeTestResults: true testRunTitle: Windows-netcore-$(targetFramework)-ManagedSNI_$(managedSNI) - buildPlatform: $(Platform) - buildConfiguration: $(Configuration) + buildConfiguration: Release - pwsh: | cd TestResults @@ -388,8 +335,10 @@ stages: fetchDepth: 1 fetchTags: false - - task: NuGetToolInstaller@1 - displayName: Use NuGet + # --- Install .NET SDK and runtimes --- + - template: /eng/pipelines/steps/install-dotnet.yml@self + parameters: + runtimes: ['8.x', '9.x', '10.x'] # --- Update test configuration (with Kerberos credentials) --- - pwsh: | @@ -427,41 +376,26 @@ stages: env: REMOTE_TCP_CONN_STRING: $(REMOTE_TCP_CONN_STRING) - # --- Install .NET SDK and runtimes --- - - template: /eng/pipelines/steps/install-dotnet.yml@self - parameters: - runtimes: ['8.x', '9.x', '10.x'] - # --- Build --- - task: DotNetCoreCLI@2 - displayName: Build Driver + displayName: Build SqlClient inputs: - command: custom + command: build projects: build.proj - custom: msbuild arguments: >- -t:BuildSqlClient - -p:ReferenceType=Project - -p:TestEnabled=true - -p:GenerateNuget=false - -p:GenerateDocumentationFile=false - -p:Configuration=$(Configuration) - -p:Platform=$(Platform) - retryCountOnTaskFailure: 1 + -p:Configuration=Release # --- Run tests --- - task: DotNetCoreCLI@2 displayName: Run Tests ($(targetFramework)) inputs: - command: custom + command: build projects: build.proj - custom: msbuild arguments: >- - -t:RunTests - -p:TF=$(targetFramework) - -p:ReferenceType=Project - -p:Configuration=$(Configuration) - -p:Platform=$(Platform) + -t:TestSqlClient + -p:TestFramework=$(targetFramework) + -p:Configuration=Release # --- Publish results & coverage --- - task: PublishTestResults@2 @@ -474,8 +408,7 @@ stages: TestResults/**/*.coverage mergeTestResults: true testRunTitle: Linux-netcore-$(targetFramework) - buildPlatform: $(Platform) - buildConfiguration: $(Configuration) + buildConfiguration: Release - pwsh: | cd TestResults From 9da3ba3ef07ec946ec046734f791affeca962527 Mon Sep 17 00:00:00 2001 From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com> Date: Thu, 30 Apr 2026 11:44:31 -0300 Subject: [PATCH 15/25] - Combined Windows stages together. - Added missing Kerberos password variable. --- eng/pipelines/sqlclient-kerberos.yml | 154 +++++---------------------- 1 file changed, 24 insertions(+), 130 deletions(-) diff --git a/eng/pipelines/sqlclient-kerberos.yml b/eng/pipelines/sqlclient-kerberos.yml index 9672de4d1e..2fccc101b2 100644 --- a/eng/pipelines/sqlclient-kerberos.yml +++ b/eng/pipelines/sqlclient-kerberos.yml @@ -50,135 +50,22 @@ name: $(date:yyyyMMdd)$(rev:.r) stages: # =========================================================================== - # Stage 1 — Windows · .NET Framework (1 job) + # Stage 1 - Windows (7 jobs = net462 + 3 TFs × 2 ManagedSNI) # =========================================================================== - - stage: Windows_netfx - displayName: Windows - .NET Framework + - stage: windows + displayName: Windows dependsOn: [] jobs: - - job: Windows_netfx_net462 - displayName: Windows netfx net462 - timeoutInMinutes: 360 - pool: - name: ADO-Trusted-Domain-Win-WestUS2 - demands: - - ImageOverride -equals ADO-MMS22-SQL19 - steps: - - - checkout: self - clean: true - fetchDepth: 1 - fetchTags: false - - # Install .NET SDK (from global.json) and runtimes using the shared - # template. - - template: /eng/pipelines/steps/install-dotnet.yml@self - parameters: - runtimes: ['8.x', '9.x'] - - # --- Update test configuration --- - - pwsh: | - $jdata = Get-Content -Raw "config.default.json" | ConvertFrom-Json - foreach ($p in $jdata) { - $p.TCPConnectionString = $env:REMOTE_TCP_CONN_STRING - $p.NPConnectionString = $env:REMOTE_NP_CONN_STRING - $p.SupportsIntegratedSecurity = "true" - $p.UseManagedSNIOnWindows = "false" - } - $jdata | ConvertTo-Json | Set-Content "config.json" - workingDirectory: src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities - displayName: Update test config.json - env: - REMOTE_TCP_CONN_STRING: $(REMOTE_TCP_CONN_STRING) - REMOTE_NP_CONN_STRING: $(REMOTE_NP_CONN_STRING) - - # --- Prepare Windows services --- - - powershell: | - $svc = Get-Service -Name SQLBrowser -ErrorAction SilentlyContinue - if ($null -ne $svc) { - Set-Service -StartupType Automatic SQLBrowser - if ($svc.Status -ne 'Running') { Start-Service SQLBrowser } - Get-Service SQLBrowser | Select-Object Name, StartType, Status - } - displayName: Start SQL Server Browser - - - powershell: | - Set-DtcNetworkSetting -DtcName "Local" ` - -InboundTransactionsEnabled $true ` - -OutboundTransactionsEnabled $true ` - -RemoteClientAccessEnabled $true ` - -Confirm:$false - - Get-NetFirewallRule -DisplayName "Distributed Transaction Coordinator (RPC)" | Set-NetFirewallRule -Profile Domain -Action Allow -Enabled True - Get-NetFirewallRule -DisplayName "Distributed Transaction Coordinator (RPC-EPMAP)" | Set-NetFirewallRule -Profile Domain -Action Allow -Enabled True - Get-NetFirewallRule -DisplayName "Distributed Transaction Coordinator (TCP-Out)" | Set-NetFirewallRule -Profile Domain -Action Allow -Enabled True - Get-NetFirewallRule -DisplayName "Distributed Transaction Coordinator (TCP-In)" | Set-NetFirewallRule -Profile Domain -Action Allow -Enabled True - displayName: Enable Network DTC Access - - # --- Build --- - - task: DotNetCoreCLI@2 - displayName: Build SqlClient - retryCountOnTaskFailure: 1 - inputs: - command: build - projects: build.proj - arguments: >- - -t:BuildSqlClient - -p:Configuration=Release - - # --- Run tests --- - - task: DotNetCoreCLI@2 - displayName: Run Tests (net462) - retryCountOnTaskFailure: 2 - inputs: - command: build - projects: build.proj - arguments: >- - -t:TestSqlClient - -p:TestFramework=net462 - -p:Configuration=Release - - # --- Publish results & coverage --- - - task: PublishTestResults@2 - displayName: Publish Test Results - condition: succeededOrFailed() - inputs: - testResultsFormat: VSTest - testResultsFiles: | - TestResults/*.trx - TestResults/**/*.coverage - mergeTestResults: true - testRunTitle: Windows-netfx-net462 - buildConfiguration: Release - - - pwsh: | - cd TestResults - Get-ChildItem -Filter "*.coverage" -Recurse | - Rename-Item -NewName { "net462" + $_.Name } - displayName: Rename coverage files - condition: succeededOrFailed() - - - task: PublishPipelineArtifact@1 - displayName: Publish Test Artifacts - condition: succeededOrFailed() - inputs: - targetPath: TestResults - artifact: net462-$(System.JobId) - - # =========================================================================== - # Stage 2 — Windows · .NET Core (6 jobs = 3 TFs × 2 ManagedSNI) - # =========================================================================== - - stage: Windows_netcore - displayName: Windows - .NET Core - dependsOn: [] - jobs: - - job: Windows_netcore - displayName: Windows netcore + - job: windows + displayName: Windows timeoutInMinutes: 360 workspace: clean: all # Purge obj/artifacts from prior runs on self-hosted agents strategy: matrix: + net462_NativeSNI: + targetFramework: net462 + managedSNI: 'false' net8_NativeSNI: targetFramework: net8.0 managedSNI: 'false' @@ -307,14 +194,14 @@ stages: artifact: $(targetFramework)-ManagedSNI_$(managedSNI)-$(System.JobId) # =========================================================================== - # Stage 3 — Linux · .NET Core + Kerberos (3 jobs = 3 TFs) + # Stage 2 - Linux - .NET Core + Kerberos (3 jobs = 3 TFs) # =========================================================================== - - stage: Linux_netcore - displayName: Linux - .NET Core (Kerberos) + - stage: linux + displayName: Linux dependsOn: [] jobs: - - job: Linux_netcore - displayName: Linux Kerberos + - job: linux + displayName: Linux timeoutInMinutes: 360 strategy: matrix: @@ -328,6 +215,14 @@ stages: name: ADO-Trusted-Linux-WestUS2 demands: - ImageOverride -equals ADO-UB20-SQL22 + + variables: + # Linux needs a secret from the $(agent-at-sqldrv-ad) secret from this group on order to + # expand the pipeline variable $(KerberosDomainPassword) at runtime. This is because the + # password is used in the bash step to acquire a Kerberos ticket, and bash cannot read ADO + # secrets directly. + group: kv-sqldrivers-shared + steps: - checkout: self @@ -338,7 +233,7 @@ stages: # --- Install .NET SDK and runtimes --- - template: /eng/pipelines/steps/install-dotnet.yml@self parameters: - runtimes: ['8.x', '9.x', '10.x'] + runtimes: ['8.x', '9.x'] # --- Update test configuration (with Kerberos credentials) --- - pwsh: | @@ -437,9 +332,8 @@ stages: - stage: Merge_Code_Coverage displayName: Merge Code Coverage dependsOn: - - Windows_netfx - - Windows_netcore - - Linux_netcore + - windows + - linux condition: succeededOrFailed() jobs: - template: /eng/pipelines/common/templates/jobs/ci-code-coverage-job.yml@self From 89e7cf3749e9e602f2e0f015a121a9b385e79e70 Mon Sep 17 00:00:00 2001 From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com> Date: Thu, 30 Apr 2026 12:28:31 -0300 Subject: [PATCH 16/25] - Moved environmental variables into the YAML from the Pipeline UI. - Restricting the build steps to use OS-specific targets. --- eng/pipelines/sqlclient-kerberos.yml | 37 +++++++++++++++++++++------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/eng/pipelines/sqlclient-kerberos.yml b/eng/pipelines/sqlclient-kerberos.yml index 2fccc101b2..a32d0ef8ec 100644 --- a/eng/pipelines/sqlclient-kerberos.yml +++ b/eng/pipelines/sqlclient-kerberos.yml @@ -44,6 +44,32 @@ schedules: name: $(date:yyyyMMdd)$(rev:.r) +variables: + # Linux needs a secret from the $(agent-at-sqldrv-ad) secret from this group on order to expand + # the pipeline variable $(KerberosDomainPassword) at runtime. This is because the password is + # used in the bash step to acquire a Kerberos ticket, and bash cannot read ADO secrets directly. + - group: kv-sqldrivers-shared + + # Our Kerberos environment doesn't change often, so we can define these variables here rather than + # in the Pipeline UI or in Libraries. + - name: KerberosDomain + value: sqldrv.ad + + - name: KerberosDomainOU + value: agents + + - name: KerberosDomainUser + value: agent + + - name: KerberosDomainPassword + value: $(agent-at-sqldrv-ad) # Secret from kv-sqldrivers-shared variable group + + - name: REMOTE_TCP_CONN_STRING + value: Data Source=tcp:sqldrv-sql22.sqldrv.ad\sql2022;Initial Catalog=Northwind;Integrated Security=true;Encrypt=false;TrustServerCertificate=true + + - name: REMOTE_NP_CONN_STRING + value: Data Source=np:sqldrv-sql22.sqldrv.ad\sql2022;Initial Catalog=Northwind;Integrated Security=true;Encrypt=false;TrustServerCertificate=true + # ============================================================================= # STAGES # ============================================================================= @@ -151,7 +177,7 @@ stages: command: build projects: build.proj arguments: >- - -t:BuildSqlClient + -t:BuildSqlClientWindows -p:Configuration=Release # --- Run tests --- @@ -216,13 +242,6 @@ stages: demands: - ImageOverride -equals ADO-UB20-SQL22 - variables: - # Linux needs a secret from the $(agent-at-sqldrv-ad) secret from this group on order to - # expand the pipeline variable $(KerberosDomainPassword) at runtime. This is because the - # password is used in the bash step to acquire a Kerberos ticket, and bash cannot read ADO - # secrets directly. - group: kv-sqldrivers-shared - steps: - checkout: self @@ -278,7 +297,7 @@ stages: command: build projects: build.proj arguments: >- - -t:BuildSqlClient + -t:BuildSqlClientUnix -p:Configuration=Release # --- Run tests --- From 93dd96f7c8a45a32a23d856c36e7990abe5082d3 Mon Sep 17 00:00:00 2001 From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com> Date: Thu, 30 Apr 2026 12:53:13 -0300 Subject: [PATCH 17/25] Fixed problem of YAML targetFramework matrix variable being implicitly used as the MSBuild TargetFramework. --- eng/pipelines/sqlclient-kerberos.yml | 44 +++++++++++++++------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/eng/pipelines/sqlclient-kerberos.yml b/eng/pipelines/sqlclient-kerberos.yml index a32d0ef8ec..793f22fa6b 100644 --- a/eng/pipelines/sqlclient-kerberos.yml +++ b/eng/pipelines/sqlclient-kerberos.yml @@ -89,26 +89,30 @@ stages: clean: all # Purge obj/artifacts from prior runs on self-hosted agents strategy: matrix: + # Azure Pipelines exposes matrix variables as environment variables for each step. + # Do not use the name targetFramework here: MSBuild imports environment variables as + # properties, and TargetFramework would leak into dotnet build build.proj, forcing + # transitive project references onto an invalid TFM (for example SqlServer.Server -> net9.0). net462_NativeSNI: - targetFramework: net462 + testFramework: net462 managedSNI: 'false' net8_NativeSNI: - targetFramework: net8.0 + testFramework: net8.0 managedSNI: 'false' net8_ManagedSNI: - targetFramework: net8.0 + testFramework: net8.0 managedSNI: 'true' net9_NativeSNI: - targetFramework: net9.0 + testFramework: net9.0 managedSNI: 'false' net9_ManagedSNI: - targetFramework: net9.0 + testFramework: net9.0 managedSNI: 'true' net10_NativeSNI: - targetFramework: net10.0 + testFramework: net10.0 managedSNI: 'false' net10_ManagedSNI: - targetFramework: net10.0 + testFramework: net10.0 managedSNI: 'true' pool: name: ADO-Trusted-Domain-Win-WestUS2 @@ -182,14 +186,14 @@ stages: # --- Run tests --- - task: DotNetCoreCLI@2 - displayName: Run Tests ($(targetFramework)) + displayName: Run Tests ($(testFramework)) retryCountOnTaskFailure: 2 inputs: command: build projects: build.proj arguments: >- -t:TestSqlClient - -p:TestFramework=$(targetFramework) + -p:TestFramework=$(testFramework) -p:Configuration=Release # --- Publish results & coverage --- @@ -202,13 +206,13 @@ stages: TestResults/*.trx TestResults/**/*.coverage mergeTestResults: true - testRunTitle: Windows-netcore-$(targetFramework)-ManagedSNI_$(managedSNI) + testRunTitle: Windows-netcore-$(testFramework)-ManagedSNI_$(managedSNI) buildConfiguration: Release - pwsh: | cd TestResults Get-ChildItem -Filter "*.coverage" -Recurse | - Rename-Item -NewName { "$(targetFramework)" + $_.Name } + Rename-Item -NewName { "$(testFramework)" + $_.Name } displayName: Rename coverage files condition: succeededOrFailed() @@ -217,7 +221,7 @@ stages: condition: succeededOrFailed() inputs: targetPath: TestResults - artifact: $(targetFramework)-ManagedSNI_$(managedSNI)-$(System.JobId) + artifact: $(testFramework)-ManagedSNI_$(managedSNI)-$(System.JobId) # =========================================================================== # Stage 2 - Linux - .NET Core + Kerberos (3 jobs = 3 TFs) @@ -232,11 +236,11 @@ stages: strategy: matrix: net8: - targetFramework: net8.0 + testFramework: net8.0 net9: - targetFramework: net9.0 + testFramework: net9.0 net10: - targetFramework: net10.0 + testFramework: net10.0 pool: name: ADO-Trusted-Linux-WestUS2 demands: @@ -302,13 +306,13 @@ stages: # --- Run tests --- - task: DotNetCoreCLI@2 - displayName: Run Tests ($(targetFramework)) + displayName: Run Tests ($(testFramework)) inputs: command: build projects: build.proj arguments: >- -t:TestSqlClient - -p:TestFramework=$(targetFramework) + -p:TestFramework=$(testFramework) -p:Configuration=Release # --- Publish results & coverage --- @@ -321,13 +325,13 @@ stages: TestResults/*.trx TestResults/**/*.coverage mergeTestResults: true - testRunTitle: Linux-netcore-$(targetFramework) + testRunTitle: Linux-netcore-$(testFramework) buildConfiguration: Release - pwsh: | cd TestResults Get-ChildItem -Filter "*.coverage" -Recurse | - Rename-Item -NewName { "$(targetFramework)" + $_.Name } + Rename-Item -NewName { "$(testFramework)" + $_.Name } displayName: Rename coverage files condition: succeededOrFailed() @@ -336,7 +340,7 @@ stages: condition: succeededOrFailed() inputs: targetPath: TestResults - artifact: $(targetFramework)-linux-$(System.JobId) + artifact: $(testFramework)-linux-$(System.JobId) # --- Kerberos cleanup (always runs) --- - template: /eng/pipelines/common/templates/steps/kerberos-cleanup-step.yml@self From bca920abf70bab68f931bf79c54b8f7bfaae64e1 Mon Sep 17 00:00:00 2001 From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com> Date: Thu, 30 Apr 2026 13:40:43 -0300 Subject: [PATCH 18/25] - Moved the pipeline files into a kerberos/ directory. - Extracted the comon build/test steps to avoid duplication. --- .../kerberos/build-and-test-steps.yml | 114 +++++++++++++++++ .../linux-cleanup-step.yml} | 2 +- .../linux-init-step.yml} | 2 +- .../{ => kerberos}/sqlclient-kerberos.yml | 119 +++--------------- 4 files changed, 134 insertions(+), 103 deletions(-) create mode 100644 eng/pipelines/kerberos/build-and-test-steps.yml rename eng/pipelines/{common/templates/steps/kerberos-cleanup-step.yml => kerberos/linux-cleanup-step.yml} (96%) rename eng/pipelines/{common/templates/steps/kerberos-init-step.yml => kerberos/linux-init-step.yml} (98%) rename eng/pipelines/{ => kerberos}/sqlclient-kerberos.yml (75%) diff --git a/eng/pipelines/kerberos/build-and-test-steps.yml b/eng/pipelines/kerberos/build-and-test-steps.yml new file mode 100644 index 0000000000..501ffa29bd --- /dev/null +++ b/eng/pipelines/kerberos/build-and-test-steps.yml @@ -0,0 +1,114 @@ +################################################################################# +# Licensed to the .NET Foundation under one or more agreements. # +# The .NET Foundation licenses this file to you under the MIT license. # +# See the LICENSE file in the project root for more information. # +################################################################################# + +# Shared build-and-test steps used by both the Windows and Linux Kerberos jobs. +# +# Parameters: +# buildTarget — The build.proj target that builds SqlClient for the current +# OS (e.g. BuildSqlClientWindows or BuildSqlClientUnix). +# testFramework — The TFM to test against (e.g. net9.0, net462). +# testRunTitle — Title for the published test results (displayed in the ADO +# Tests tab). +# artifactName — Name of the published pipeline artifact that carries the +# test results and coverage files. + +parameters: + + # build.proj target to build SqlClient for the current OS. + - name: buildTarget + type: string + + # TFM to pass to the test targets (-p:TestFramework). + - name: testFramework + type: string + + # Title shown in the ADO Tests tab for this run. + - name: testRunTitle + type: string + + # Pipeline artifact name for test results and coverage. + - name: artifactName + type: string + +steps: + + # --------------------------------------------------------------------------- + # Build + # --------------------------------------------------------------------------- + - task: DotNetCoreCLI@2 + displayName: Build SqlClient + retryCountOnTaskFailure: 1 + inputs: + command: build + projects: build.proj + arguments: >- + -t:${{ parameters.buildTarget }} + -p:Configuration=Release + + # --------------------------------------------------------------------------- + # Run tests in separate steps to permit focused retries. + # --------------------------------------------------------------------------- + - task: DotNetCoreCLI@2 + displayName: Run Unit Tests (${{ parameters.testFramework }}) + retryCountOnTaskFailure: 2 + inputs: + command: build + projects: build.proj + arguments: >- + -t:TestSqlClientUnit + -p:TestFramework=${{ parameters.testFramework }} + -p:Configuration=Release + + - task: DotNetCoreCLI@2 + displayName: Run Functional Tests (${{ parameters.testFramework }}) + retryCountOnTaskFailure: 2 + inputs: + command: build + projects: build.proj + arguments: >- + -t:TestSqlClientFunctional + -p:TestFramework=${{ parameters.testFramework }} + -p:Configuration=Release + + - task: DotNetCoreCLI@2 + displayName: Run Manual Tests (${{ parameters.testFramework }}) + retryCountOnTaskFailure: 2 + inputs: + command: build + projects: build.proj + arguments: >- + -t:TestSqlClientManual + -p:TestFramework=${{ parameters.testFramework }} + -p:Configuration=Release + + # --------------------------------------------------------------------------- + # Publish results & coverage + # --------------------------------------------------------------------------- + - task: PublishTestResults@2 + displayName: Publish Test Results + condition: succeededOrFailed() + inputs: + testResultsFormat: VSTest + testResultsFiles: | + TestResults/*.trx + TestResults/**/*.coverage + mergeTestResults: true + testRunTitle: ${{ parameters.testRunTitle }} + buildConfiguration: Release + + - pwsh: | + cd TestResults + Get-ChildItem -Filter "*.coverage" -Recurse | + Rename-Item -NewName { "${{ parameters.testFramework }}" + $_.Name } + displayName: Rename coverage files + condition: succeededOrFailed() + + - task: PublishPipelineArtifact@1 + displayName: Publish Test Artifacts + condition: succeededOrFailed() + inputs: + targetPath: TestResults + artifact: ${{ parameters.artifactName }} diff --git a/eng/pipelines/common/templates/steps/kerberos-cleanup-step.yml b/eng/pipelines/kerberos/linux-cleanup-step.yml similarity index 96% rename from eng/pipelines/common/templates/steps/kerberos-cleanup-step.yml rename to eng/pipelines/kerberos/linux-cleanup-step.yml index 98985f44c6..3de9486aab 100644 --- a/eng/pipelines/common/templates/steps/kerberos-cleanup-step.yml +++ b/eng/pipelines/kerberos/linux-cleanup-step.yml @@ -41,5 +41,5 @@ steps: # Destroy the TGT and credential cache kdestroy || true - displayName: 'Clean up Kerberos (domain leave + kdestroy)' + displayName: Clean up Kerberos (domain leave + kdestroy) condition: always() diff --git a/eng/pipelines/common/templates/steps/kerberos-init-step.yml b/eng/pipelines/kerberos/linux-init-step.yml similarity index 98% rename from eng/pipelines/common/templates/steps/kerberos-init-step.yml rename to eng/pipelines/kerberos/linux-init-step.yml index d05a7727d0..dad6a184f3 100644 --- a/eng/pipelines/common/templates/steps/kerberos-init-step.yml +++ b/eng/pipelines/kerberos/linux-init-step.yml @@ -106,4 +106,4 @@ steps: klist sudo ifconfig - displayName: 'Initialize Kerberos (domain join + kinit)' + displayName: Initialize Kerberos (domain join + kinit) diff --git a/eng/pipelines/sqlclient-kerberos.yml b/eng/pipelines/kerberos/sqlclient-kerberos.yml similarity index 75% rename from eng/pipelines/sqlclient-kerberos.yml rename to eng/pipelines/kerberos/sqlclient-kerberos.yml index 793f22fa6b..d15b04c3a3 100644 --- a/eng/pipelines/sqlclient-kerberos.yml +++ b/eng/pipelines/kerberos/sqlclient-kerberos.yml @@ -127,7 +127,7 @@ stages: - template: /eng/pipelines/steps/install-dotnet.yml@self parameters: - runtimes: ['8.x', '9.x'] + runtimes: [8.x, 9.x] # --- Update test configuration --- # Uses runtime variables from the matrix ($(managedSNI)) so we use @@ -172,56 +172,13 @@ stages: Get-NetFirewallRule -DisplayName "Distributed Transaction Coordinator (TCP-In)" | Set-NetFirewallRule -Profile Domain -Action Allow -Enabled True displayName: Enable Network DTC Access - # --- Build --- - - - task: DotNetCoreCLI@2 - displayName: Build SqlClient - retryCountOnTaskFailure: 1 - inputs: - command: build - projects: build.proj - arguments: >- - -t:BuildSqlClientWindows - -p:Configuration=Release - - # --- Run tests --- - - task: DotNetCoreCLI@2 - displayName: Run Tests ($(testFramework)) - retryCountOnTaskFailure: 2 - inputs: - command: build - projects: build.proj - arguments: >- - -t:TestSqlClient - -p:TestFramework=$(testFramework) - -p:Configuration=Release - - # --- Publish results & coverage --- - - task: PublishTestResults@2 - displayName: Publish Test Results - condition: succeededOrFailed() - inputs: - testResultsFormat: VSTest - testResultsFiles: | - TestResults/*.trx - TestResults/**/*.coverage - mergeTestResults: true - testRunTitle: Windows-netcore-$(testFramework)-ManagedSNI_$(managedSNI) - buildConfiguration: Release - - - pwsh: | - cd TestResults - Get-ChildItem -Filter "*.coverage" -Recurse | - Rename-Item -NewName { "$(testFramework)" + $_.Name } - displayName: Rename coverage files - condition: succeededOrFailed() - - - task: PublishPipelineArtifact@1 - displayName: Publish Test Artifacts - condition: succeededOrFailed() - inputs: - targetPath: TestResults - artifact: $(testFramework)-ManagedSNI_$(managedSNI)-$(System.JobId) + # --- Build and test --- + - template: /eng/pipelines/kerberos/build-and-test-steps.yml@self + parameters: + buildTarget: BuildSqlClientWindows + testFramework: $(testFramework) + testRunTitle: Windows-$(testFramework)-ManagedSNI_$(managedSNI) + artifactName: $(testFramework)-ManagedSNI_$(managedSNI)-$(System.JobId) # =========================================================================== # Stage 2 - Linux - .NET Core + Kerberos (3 jobs = 3 TFs) @@ -256,7 +213,7 @@ stages: # --- Install .NET SDK and runtimes --- - template: /eng/pipelines/steps/install-dotnet.yml@self parameters: - runtimes: ['8.x', '9.x'] + runtimes: [8.x, 9.x] # --- Update test configuration (with Kerberos credentials) --- - pwsh: | @@ -278,7 +235,7 @@ stages: KERBEROS_DOMAIN_PASSWORD: $(KerberosDomainPassword) # --- Kerberos domain join --- - - template: /eng/pipelines/common/templates/steps/kerberos-init-step.yml@self + - template: /eng/pipelines/kerberos/linux-init-step.yml@self parameters: kerberosDomain: $(KerberosDomain) kerberosDomainOU: $(KerberosDomainOU) @@ -294,56 +251,16 @@ stages: env: REMOTE_TCP_CONN_STRING: $(REMOTE_TCP_CONN_STRING) - # --- Build --- - - task: DotNetCoreCLI@2 - displayName: Build SqlClient - inputs: - command: build - projects: build.proj - arguments: >- - -t:BuildSqlClientUnix - -p:Configuration=Release - - # --- Run tests --- - - task: DotNetCoreCLI@2 - displayName: Run Tests ($(testFramework)) - inputs: - command: build - projects: build.proj - arguments: >- - -t:TestSqlClient - -p:TestFramework=$(testFramework) - -p:Configuration=Release - - # --- Publish results & coverage --- - - task: PublishTestResults@2 - displayName: Publish Test Results - condition: succeededOrFailed() - inputs: - testResultsFormat: VSTest - testResultsFiles: | - TestResults/*.trx - TestResults/**/*.coverage - mergeTestResults: true - testRunTitle: Linux-netcore-$(testFramework) - buildConfiguration: Release - - - pwsh: | - cd TestResults - Get-ChildItem -Filter "*.coverage" -Recurse | - Rename-Item -NewName { "$(testFramework)" + $_.Name } - displayName: Rename coverage files - condition: succeededOrFailed() - - - task: PublishPipelineArtifact@1 - displayName: Publish Test Artifacts - condition: succeededOrFailed() - inputs: - targetPath: TestResults - artifact: $(testFramework)-linux-$(System.JobId) + # --- Build and test --- + - template: /eng/pipelines/kerberos/build-and-test-steps.yml@self + parameters: + buildTarget: BuildSqlClientUnix + testFramework: $(testFramework) + testRunTitle: Linux-$(testFramework) + artifactName: $(testFramework)-linux-$(System.JobId) # --- Kerberos cleanup (always runs) --- - - template: /eng/pipelines/common/templates/steps/kerberos-cleanup-step.yml@self + - template: /eng/pipelines/kerberos/linux-cleanup-step.yml@self parameters: kerberosDomain: $(KerberosDomain) kerberosDomainUser: $(KerberosDomainUser) From f5fdd5f0a7c8f48028db643d81e91ed85f85b300 Mon Sep 17 00:00:00 2001 From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com> Date: Thu, 30 Apr 2026 14:50:30 -0300 Subject: [PATCH 19/25] - Fixed test results and coverage file processing. - Reduced job timeout to 90 minutes from 360 (6 hours!). --- .../kerberos/build-and-test-steps.yml | 10 +++-- eng/pipelines/kerberos/sqlclient-kerberos.yml | 37 +++++++++++-------- 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/eng/pipelines/kerberos/build-and-test-steps.yml b/eng/pipelines/kerberos/build-and-test-steps.yml index 501ffa29bd..e8437606fb 100644 --- a/eng/pipelines/kerberos/build-and-test-steps.yml +++ b/eng/pipelines/kerberos/build-and-test-steps.yml @@ -92,15 +92,17 @@ steps: condition: succeededOrFailed() inputs: testResultsFormat: VSTest + # build.proj defines TestResultsFolderPath which defaults to + # $(Build.SourcesDirectory)/test_results, so we look there for the results and coverage files. testResultsFiles: | - TestResults/*.trx - TestResults/**/*.coverage + $(Build.SourcesDirectory)/test_results/*.trx + $(Build.SourcesDirectory)/test_results/**/*.coverage mergeTestResults: true testRunTitle: ${{ parameters.testRunTitle }} buildConfiguration: Release - pwsh: | - cd TestResults + cd $(Build.SourcesDirectory)/test_results Get-ChildItem -Filter "*.coverage" -Recurse | Rename-Item -NewName { "${{ parameters.testFramework }}" + $_.Name } displayName: Rename coverage files @@ -110,5 +112,5 @@ steps: displayName: Publish Test Artifacts condition: succeededOrFailed() inputs: - targetPath: TestResults + targetPath: $(Build.SourcesDirectory)/test_results artifact: ${{ parameters.artifactName }} diff --git a/eng/pipelines/kerberos/sqlclient-kerberos.yml b/eng/pipelines/kerberos/sqlclient-kerberos.yml index d15b04c3a3..488a888861 100644 --- a/eng/pipelines/kerberos/sqlclient-kerberos.yml +++ b/eng/pipelines/kerberos/sqlclient-kerberos.yml @@ -7,26 +7,31 @@ # ============================================================================= # sqlclient-kerberos # ============================================================================= -# Nightly Kerberos authentication test pipeline for Microsoft.Data.SqlClient. +# Daily Kerberos authentication test pipeline for Microsoft.Data.SqlClient. # Replaces the Classic "Test-SqlClient-Kerberos-Azure" pipeline. # -# Job breakdown (11 total): -# Stage: Windows-netfx → 1 job (net462) -# Stage: Windows-netcore → 6 jobs (3 TFs × 2 ManagedSNI) -# Stage: Linux-netcore → 3 jobs (3 TFs, ManagedSNI=True) +# Schedule: daily at 07:00 UTC on main, release/6.1, and release/7.0. +# +# Job breakdown (10 total): +# Stage: windows → 7 jobs (net462 + net8/9/10 × NativeSNI/ManagedSNI) +# Stage: linux → 3 jobs (net8, net9, net10 — ManagedSNI only) # Stage: Merge-Code-Coverage → 1 job # +# Shared steps: +# Build and test steps are defined in build-and-test-steps.yml and reused +# by both stages. Each stage passes its OS-specific build target and the +# per-job testFramework matrix variable. +# # Required ADO variable groups (link in pipeline UI): -# - kv-sqldrivers-shared -# - ADO - SQL Server Setup variables +# - kv-sqldrivers-shared (provides the agent-at-sqldrv-ad secret) # -# Required ADO pipeline variables (define in pipeline UI): -# - REMOTE_TCP_CONN_STRING (connection string for remote SQL via TCP) -# - REMOTE_NP_CONN_STRING (connection string for remote SQL via Named Pipes) -# - KerberosDomain (AD domain, e.g. mydomain.contoso.com) -# - KerberosDomainOU (OU for computer accounts) -# - KerberosDomainUser (domain user, sAMAccountName) -# - KerberosDomainPassword (secret — domain user password) +# Variables defined in this file (no pipeline UI configuration needed): +# - KerberosDomain sqldrv.ad +# - KerberosDomainOU agents +# - KerberosDomainUser agent +# - KerberosDomainPassword $(agent-at-sqldrv-ad) from kv-sqldrivers-shared +# - REMOTE_TCP_CONN_STRING TCP connection string to sqldrv-sql22 +# - REMOTE_NP_CONN_STRING Named Pipe connection string to sqldrv-sql22 # ============================================================================= trigger: none # Nightly only — no CI trigger @@ -84,7 +89,7 @@ stages: jobs: - job: windows displayName: Windows - timeoutInMinutes: 360 + timeoutInMinutes: 90 workspace: clean: all # Purge obj/artifacts from prior runs on self-hosted agents strategy: @@ -189,7 +194,7 @@ stages: jobs: - job: linux displayName: Linux - timeoutInMinutes: 360 + timeoutInMinutes: 90 strategy: matrix: net8: From 86457d764a65709e0d4d51bc9b7e559cbad93f24 Mon Sep 17 00:00:00 2001 From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com> Date: Thu, 30 Apr 2026 16:18:41 -0300 Subject: [PATCH 20/25] Fix Kerberos pipeline review feedback --- .../kerberos/build-and-test-steps.yml | 20 +++++++++++++--- eng/pipelines/kerberos/linux-cleanup-step.yml | 2 +- eng/pipelines/kerberos/linux-init-step.yml | 3 ++- eng/pipelines/kerberos/sqlclient-kerberos.yml | 24 +++++++++++-------- 4 files changed, 34 insertions(+), 15 deletions(-) diff --git a/eng/pipelines/kerberos/build-and-test-steps.yml b/eng/pipelines/kerberos/build-and-test-steps.yml index e8437606fb..5833d60f46 100644 --- a/eng/pipelines/kerberos/build-and-test-steps.yml +++ b/eng/pipelines/kerberos/build-and-test-steps.yml @@ -38,6 +38,12 @@ steps: # --------------------------------------------------------------------------- # Build # --------------------------------------------------------------------------- + + # Build the given target. + # + # The test stages will build this anyway, but it's nice to have a separate step to isolate build + # failures and avoid retries. + # - task: DotNetCoreCLI@2 displayName: Build SqlClient retryCountOnTaskFailure: 1 @@ -51,6 +57,8 @@ steps: # --------------------------------------------------------------------------- # Run tests in separate steps to permit focused retries. # --------------------------------------------------------------------------- + + # Run the Unit Test suite. - task: DotNetCoreCLI@2 displayName: Run Unit Tests (${{ parameters.testFramework }}) retryCountOnTaskFailure: 2 @@ -62,6 +70,7 @@ steps: -p:TestFramework=${{ parameters.testFramework }} -p:Configuration=Release + # Run the Functional Test suite. - task: DotNetCoreCLI@2 displayName: Run Functional Tests (${{ parameters.testFramework }}) retryCountOnTaskFailure: 2 @@ -73,6 +82,7 @@ steps: -p:TestFramework=${{ parameters.testFramework }} -p:Configuration=Release + # Run the Manual Test suite. - task: DotNetCoreCLI@2 displayName: Run Manual Tests (${{ parameters.testFramework }}) retryCountOnTaskFailure: 2 @@ -87,6 +97,8 @@ steps: # --------------------------------------------------------------------------- # Publish results & coverage # --------------------------------------------------------------------------- + + # Publish the TRX test results to the pipeline run. - task: PublishTestResults@2 displayName: Publish Test Results condition: succeededOrFailed() @@ -94,13 +106,13 @@ steps: testResultsFormat: VSTest # build.proj defines TestResultsFolderPath which defaults to # $(Build.SourcesDirectory)/test_results, so we look there for the results and coverage files. - testResultsFiles: | - $(Build.SourcesDirectory)/test_results/*.trx - $(Build.SourcesDirectory)/test_results/**/*.coverage + testResultsFiles: $(Build.SourcesDirectory)/test_results/**/*.trx mergeTestResults: true testRunTitle: ${{ parameters.testRunTitle }} buildConfiguration: Release + # Give our coverage files a unique name to make it clear where they originated when we download + # the artifacts from all jobs in the merge stage. - pwsh: | cd $(Build.SourcesDirectory)/test_results Get-ChildItem -Filter "*.coverage" -Recurse | @@ -108,6 +120,8 @@ steps: displayName: Rename coverage files condition: succeededOrFailed() + # Publish TRX test results and coverage files as pipeline artifacts. The merge stage needs the + # coverage files from all of the jobs. - task: PublishPipelineArtifact@1 displayName: Publish Test Artifacts condition: succeededOrFailed() diff --git a/eng/pipelines/kerberos/linux-cleanup-step.yml b/eng/pipelines/kerberos/linux-cleanup-step.yml index 3de9486aab..d3491bb337 100644 --- a/eng/pipelines/kerberos/linux-cleanup-step.yml +++ b/eng/pipelines/kerberos/linux-cleanup-step.yml @@ -6,7 +6,7 @@ # This template leaves the Active Directory domain and destroys Kerberos # credentials. It should be referenced at the end of any job that called -# kerberos-init-step.yml. +# linux-init-step.yml. # # All steps use condition: always() so that cleanup runs even when previous # steps fail. diff --git a/eng/pipelines/kerberos/linux-init-step.yml b/eng/pipelines/kerberos/linux-init-step.yml index dad6a184f3..4df83d33d8 100644 --- a/eng/pipelines/kerberos/linux-init-step.yml +++ b/eng/pipelines/kerberos/linux-init-step.yml @@ -105,5 +105,6 @@ steps: echo "$DOMAIN_PASSWORD" | kinit "$DOMAIN_USER@$DOMAIN_UPPER" klist - sudo ifconfig + sudo ip addr + sudo ip route displayName: Initialize Kerberos (domain join + kinit) diff --git a/eng/pipelines/kerberos/sqlclient-kerberos.yml b/eng/pipelines/kerberos/sqlclient-kerberos.yml index 488a888861..8694aadc8e 100644 --- a/eng/pipelines/kerberos/sqlclient-kerberos.yml +++ b/eng/pipelines/kerberos/sqlclient-kerberos.yml @@ -34,7 +34,7 @@ # - REMOTE_NP_CONN_STRING Named Pipe connection string to sqldrv-sql22 # ============================================================================= -trigger: none # Nightly only — no CI trigger +trigger: none # Scheduled runs only — no CI trigger pr: none # Not triggered by PRs schedules: @@ -50,11 +50,6 @@ schedules: name: $(date:yyyyMMdd)$(rev:.r) variables: - # Linux needs a secret from the $(agent-at-sqldrv-ad) secret from this group on order to expand - # the pipeline variable $(KerberosDomainPassword) at runtime. This is because the password is - # used in the bash step to acquire a Kerberos ticket, and bash cannot read ADO secrets directly. - - group: kv-sqldrivers-shared - # Our Kerberos environment doesn't change often, so we can define these variables here rather than # in the Pipeline UI or in Libraries. - name: KerberosDomain @@ -86,6 +81,10 @@ stages: - stage: windows displayName: Windows dependsOn: [] + variables: + # KerberosDomainPassword expands at runtime in this stage from the agent-at-sqldrv-ad secret + # exposed by this variable group. + - group: kv-sqldrivers-shared jobs: - job: windows displayName: Windows @@ -139,12 +138,13 @@ stages: # inline PowerShell instead of the shared config template which # requires compile-time parameters. - pwsh: | + $managedSni = [System.Convert]::ToBoolean($env:MANAGED_SNI) $jdata = Get-Content -Raw "config.default.json" | ConvertFrom-Json foreach ($p in $jdata) { $p.TCPConnectionString = $env:REMOTE_TCP_CONN_STRING $p.NPConnectionString = $env:REMOTE_NP_CONN_STRING - $p.SupportsIntegratedSecurity = "true" - $p.UseManagedSNIOnWindows = $env:MANAGED_SNI + $p.SupportsIntegratedSecurity = $true + $p.UseManagedSNIOnWindows = $managedSni } $jdata | ConvertTo-Json | Set-Content "config.json" workingDirectory: src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities @@ -191,6 +191,10 @@ stages: - stage: linux displayName: Linux dependsOn: [] + variables: + # KerberosDomainPassword expands at runtime in this stage from the agent-at-sqldrv-ad secret + # exposed by this variable group. + - group: kv-sqldrivers-shared jobs: - job: linux displayName: Linux @@ -272,9 +276,9 @@ stages: kerberosDomainPassword: $(KerberosDomainPassword) # =========================================================================== - # Stage 4 — Merge Code Coverage (1 job) + # Stage 3 — Merge Code Coverage (1 job) # =========================================================================== - - stage: Merge_Code_Coverage + - stage: merge displayName: Merge Code Coverage dependsOn: - windows From abc52e3c2f6f21934e9676e28c6d6688305ac469 Mon Sep 17 00:00:00 2001 From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com> Date: Thu, 30 Apr 2026 16:21:37 -0300 Subject: [PATCH 21/25] - Excluding kerberos pipeline files from PR checks. --- eng/pipelines/sqlclient-pr-package-ref-pipeline.yml | 1 + eng/pipelines/sqlclient-pr-project-ref-pipeline.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/eng/pipelines/sqlclient-pr-package-ref-pipeline.yml b/eng/pipelines/sqlclient-pr-package-ref-pipeline.yml index ba2ff262bf..be7e2e9e3b 100644 --- a/eng/pipelines/sqlclient-pr-package-ref-pipeline.yml +++ b/eng/pipelines/sqlclient-pr-package-ref-pipeline.yml @@ -51,6 +51,7 @@ pr: - global.json - NuGet.config exclude: + - eng/pipelines/kerberos/* - eng/pipelines/onebranch/* - eng/pipelines/stress/* diff --git a/eng/pipelines/sqlclient-pr-project-ref-pipeline.yml b/eng/pipelines/sqlclient-pr-project-ref-pipeline.yml index a928175f17..30950ec21c 100644 --- a/eng/pipelines/sqlclient-pr-project-ref-pipeline.yml +++ b/eng/pipelines/sqlclient-pr-project-ref-pipeline.yml @@ -51,6 +51,7 @@ pr: - global.json - NuGet.config exclude: + - eng/pipelines/kerberos/* - eng/pipelines/onebranch/* - eng/pipelines/stress/* From 52773cd1557e2a569dce7765e14fcc019b5891b2 Mon Sep 17 00:00:00 2001 From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com> Date: Fri, 1 May 2026 08:28:44 -0300 Subject: [PATCH 22/25] Address additional Copilot pipeline review feedback --- eng/pipelines/kerberos/build-and-test-steps.yml | 10 ++++++++-- eng/pipelines/kerberos/linux-init-step.yml | 4 +++- eng/pipelines/kerberos/sqlclient-kerberos.yml | 4 +++- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/eng/pipelines/kerberos/build-and-test-steps.yml b/eng/pipelines/kerberos/build-and-test-steps.yml index 5833d60f46..d937132f3c 100644 --- a/eng/pipelines/kerberos/build-and-test-steps.yml +++ b/eng/pipelines/kerberos/build-and-test-steps.yml @@ -114,7 +114,13 @@ steps: # Give our coverage files a unique name to make it clear where they originated when we download # the artifacts from all jobs in the merge stage. - pwsh: | - cd $(Build.SourcesDirectory)/test_results + $resultsDir = "$(Build.SourcesDirectory)/test_results" + if (-not (Test-Path -LiteralPath $resultsDir)) { + Write-Host "No test_results directory found; skipping coverage rename." + exit 0 + } + + cd $resultsDir Get-ChildItem -Filter "*.coverage" -Recurse | Rename-Item -NewName { "${{ parameters.testFramework }}" + $_.Name } displayName: Rename coverage files @@ -124,7 +130,7 @@ steps: # coverage files from all of the jobs. - task: PublishPipelineArtifact@1 displayName: Publish Test Artifacts - condition: succeededOrFailed() + condition: and(succeededOrFailed(), exists('$(Build.SourcesDirectory)/test_results')) inputs: targetPath: $(Build.SourcesDirectory)/test_results artifact: ${{ parameters.artifactName }} diff --git a/eng/pipelines/kerberos/linux-init-step.yml b/eng/pipelines/kerberos/linux-init-step.yml index 4df83d33d8..ed91e33dc2 100644 --- a/eng/pipelines/kerberos/linux-init-step.yml +++ b/eng/pipelines/kerberos/linux-init-step.yml @@ -76,7 +76,9 @@ steps: # ----------------------------------------------------------------------- # Synchronize time with the domain controller (required for Kerberos) # ----------------------------------------------------------------------- - echo "server $DOMAIN" | sudo tee -a /etc/ntp.conf + if ! sudo grep -Fqx "server $DOMAIN" /etc/ntp.conf; then + echo "server $DOMAIN" | sudo tee -a /etc/ntp.conf + fi sudo systemctl stop ntp sudo ntpdate "$DOMAIN" sudo systemctl start ntp diff --git a/eng/pipelines/kerberos/sqlclient-kerberos.yml b/eng/pipelines/kerberos/sqlclient-kerberos.yml index 8694aadc8e..1cd19961e7 100644 --- a/eng/pipelines/kerberos/sqlclient-kerberos.yml +++ b/eng/pipelines/kerberos/sqlclient-kerberos.yml @@ -199,6 +199,8 @@ stages: - job: linux displayName: Linux timeoutInMinutes: 90 + workspace: + clean: all # Purge leftovers on self-hosted agents to reduce cross-run flakiness strategy: matrix: net8: @@ -230,7 +232,7 @@ stages: foreach ($p in $jdata) { $p.TCPConnectionString = $env:REMOTE_TCP_CONN_STRING $p.NPConnectionString = $env:REMOTE_NP_CONN_STRING - $p.SupportsIntegratedSecurity = "true" + $p.SupportsIntegratedSecurity = $true } $jdata | Add-Member -NotePropertyName "KerberosDomainUser" -NotePropertyValue $env:KERBEROS_DOMAIN_USER -Force $jdata | Add-Member -NotePropertyName "KerberosDomainPassword" -NotePropertyValue $env:KERBEROS_DOMAIN_PASSWORD -Force From 6424e3708356ec1ec6ca77d45ff8149d11d90ba9 Mon Sep 17 00:00:00 2001 From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com> Date: Fri, 1 May 2026 08:37:26 -0300 Subject: [PATCH 23/25] Use YAML exists condition for coverage rename step --- eng/pipelines/kerberos/build-and-test-steps.yml | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/eng/pipelines/kerberos/build-and-test-steps.yml b/eng/pipelines/kerberos/build-and-test-steps.yml index d937132f3c..bd1047970b 100644 --- a/eng/pipelines/kerberos/build-and-test-steps.yml +++ b/eng/pipelines/kerberos/build-and-test-steps.yml @@ -114,17 +114,11 @@ steps: # Give our coverage files a unique name to make it clear where they originated when we download # the artifacts from all jobs in the merge stage. - pwsh: | - $resultsDir = "$(Build.SourcesDirectory)/test_results" - if (-not (Test-Path -LiteralPath $resultsDir)) { - Write-Host "No test_results directory found; skipping coverage rename." - exit 0 - } - - cd $resultsDir + cd $(Build.SourcesDirectory)/test_results Get-ChildItem -Filter "*.coverage" -Recurse | Rename-Item -NewName { "${{ parameters.testFramework }}" + $_.Name } displayName: Rename coverage files - condition: succeededOrFailed() + condition: and(succeededOrFailed(), exists('$(Build.SourcesDirectory)/test_results')) # Publish TRX test results and coverage files as pipeline artifacts. The merge stage needs the # coverage files from all of the jobs. From 00556fade04fe9158077d6bc14183f7279d9d623 Mon Sep 17 00:00:00 2001 From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com> Date: Fri, 1 May 2026 08:39:11 -0300 Subject: [PATCH 24/25] Use runtime variable for test_results directory checks --- eng/pipelines/kerberos/build-and-test-steps.yml | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/eng/pipelines/kerberos/build-and-test-steps.yml b/eng/pipelines/kerberos/build-and-test-steps.yml index bd1047970b..d7231edbaf 100644 --- a/eng/pipelines/kerberos/build-and-test-steps.yml +++ b/eng/pipelines/kerberos/build-and-test-steps.yml @@ -111,6 +111,19 @@ steps: testRunTitle: ${{ parameters.testRunTitle }} buildConfiguration: Release + # Azure Pipelines task conditions do not support path existence checks directly, + # so compute this once and gate later steps on the variable. + - pwsh: | + $resultsDir = "$(Build.SourcesDirectory)/test_results" + if (Test-Path -LiteralPath $resultsDir) { + Write-Host "##vso[task.setvariable variable=HasTestResultsDir]true" + } + else { + Write-Host "##vso[task.setvariable variable=HasTestResultsDir]false" + } + displayName: Detect test_results directory + condition: succeededOrFailed() + # Give our coverage files a unique name to make it clear where they originated when we download # the artifacts from all jobs in the merge stage. - pwsh: | @@ -118,13 +131,13 @@ steps: Get-ChildItem -Filter "*.coverage" -Recurse | Rename-Item -NewName { "${{ parameters.testFramework }}" + $_.Name } displayName: Rename coverage files - condition: and(succeededOrFailed(), exists('$(Build.SourcesDirectory)/test_results')) + condition: and(succeededOrFailed(), eq(variables['HasTestResultsDir'], 'true')) # Publish TRX test results and coverage files as pipeline artifacts. The merge stage needs the # coverage files from all of the jobs. - task: PublishPipelineArtifact@1 displayName: Publish Test Artifacts - condition: and(succeededOrFailed(), exists('$(Build.SourcesDirectory)/test_results')) + condition: and(succeededOrFailed(), eq(variables['HasTestResultsDir'], 'true')) inputs: targetPath: $(Build.SourcesDirectory)/test_results artifact: ${{ parameters.artifactName }} From 159d457e7ddee71b0446aeab37895869664a19be Mon Sep 17 00:00:00 2001 From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com> Date: Fri, 1 May 2026 08:48:37 -0300 Subject: [PATCH 25/25] Address latest Copilot pipeline suggestions --- eng/pipelines/kerberos/build-and-test-steps.yml | 5 +++-- eng/pipelines/kerberos/linux-init-step.yml | 7 ++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/eng/pipelines/kerberos/build-and-test-steps.yml b/eng/pipelines/kerberos/build-and-test-steps.yml index d7231edbaf..307c93f978 100644 --- a/eng/pipelines/kerberos/build-and-test-steps.yml +++ b/eng/pipelines/kerberos/build-and-test-steps.yml @@ -41,8 +41,9 @@ steps: # Build the given target. # - # The test stages will build this anyway, but it's nice to have a separate step to isolate build - # failures and avoid retries. + # The test stages build as part of their targets, but this separate step isolates build failures + # so we can fail fast before running tests. Retries are enabled intentionally (1 attempt for + # build, 2 attempts for test steps) to reduce transient infrastructure-related failures. # - task: DotNetCoreCLI@2 displayName: Build SqlClient diff --git a/eng/pipelines/kerberos/linux-init-step.yml b/eng/pipelines/kerberos/linux-init-step.yml index ed91e33dc2..c0c4603232 100644 --- a/eng/pipelines/kerberos/linux-init-step.yml +++ b/eng/pipelines/kerberos/linux-init-step.yml @@ -71,7 +71,12 @@ steps: # ----------------------------------------------------------------------- # Set the hostname to FQDN within the domain # ----------------------------------------------------------------------- - sudo hostnamectl set-hostname "$(hostname).$DOMAIN" + CURRENT_HOSTNAME="$(hostname)" + if [ "$CURRENT_HOSTNAME" = "$DOMAIN" ] || [[ "$CURRENT_HOSTNAME" == *".$DOMAIN" ]]; then + echo "Hostname already uses domain suffix '.$DOMAIN': $CURRENT_HOSTNAME" + else + sudo hostnamectl set-hostname "$CURRENT_HOSTNAME.$DOMAIN" + fi # ----------------------------------------------------------------------- # Synchronize time with the domain controller (required for Kerberos)