Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
fd6a759
Convert Kerberos pipeline from Classic to YAML (11 jobs)
priyankatiwari08 Mar 10, 2026
44d6f22
fix: use env mapping for secret variables in pwsh inline scripts
priyankatiwari08 Mar 16, 2026
e234360
fix: use correct build.proj targets
priyankatiwari08 Mar 16, 2026
4e5fefd
fix: use BuildSqlClient instead of BuildAllConfigurations for Windows
priyankatiwari08 Mar 17, 2026
d99a9d1
fix: use BuildNetFx for netfx, DotNetCoreCLI for netcore on Windows
priyankatiwari08 Mar 17, 2026
5036d10
fix: add separate restore step for Windows netcore stage
priyankatiwari08 Mar 17, 2026
0dc88aa
fix: add clean step to remove stale obj/artifacts before build
priyankatiwari08 Mar 17, 2026
c3310e9
fix: switch Windows netcore to MSBuild@1 + BuildAllConfigurations + B…
priyankatiwari08 Mar 18, 2026
f0eeebb
fix: use BuildNetCore target for Windows netcore stage
priyankatiwari08 Mar 18, 2026
ed91fcd
fix: bypass build.proj for Windows netcore build step
priyankatiwari08 Mar 18, 2026
2952136
fix: clean stale obj dirs + explicit restore for Windows netcore
priyankatiwari08 Mar 18, 2026
41c3ab7
fix: workaround P2P TFM negotiation failure on ADO agents
priyankatiwari08 Mar 18, 2026
c27e6d2
fix: Replace DotNetCoreCLI@2 csproj build with MSBuild@1 build.proj -…
priyankatiwari08 Apr 9, 2026
fa16c85
Updated to use the new build.proj targets and the .NET SDK exclusively.
paulmedynski Apr 30, 2026
9da3ba3
- Combined Windows stages together.
paulmedynski Apr 30, 2026
89e7cf3
- Moved environmental variables into the YAML from the Pipeline UI.
paulmedynski Apr 30, 2026
93dd96f
Fixed problem of YAML targetFramework matrix variable being implicitl…
paulmedynski Apr 30, 2026
bca920a
- Moved the pipeline files into a kerberos/ directory.
paulmedynski Apr 30, 2026
f5fdd5f
- Fixed test results and coverage file processing.
paulmedynski Apr 30, 2026
86457d7
Fix Kerberos pipeline review feedback
paulmedynski Apr 30, 2026
abc52e3
- Excluding kerberos pipeline files from PR checks.
paulmedynski Apr 30, 2026
52773cd
Address additional Copilot pipeline review feedback
paulmedynski May 1, 2026
6424e37
Use YAML exists condition for coverage rename step
paulmedynski May 1, 2026
00556fa
Use runtime variable for test_results directory checks
paulmedynski May 1, 2026
159d457
Address latest Copilot pipeline suggestions
paulmedynski May 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 144 additions & 0 deletions eng/pipelines/kerberos/build-and-test-steps.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
#################################################################################
# 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
# ---------------------------------------------------------------------------

# Build the given target.
#
# 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
retryCountOnTaskFailure: 1
inputs:
Comment thread
paulmedynski marked this conversation as resolved.
command: build
projects: build.proj
arguments: >-
-t:${{ parameters.buildTarget }}
-p:Configuration=Release

Comment thread
paulmedynski marked this conversation as resolved.
# ---------------------------------------------------------------------------
# Run tests in separate steps to permit focused retries.
# ---------------------------------------------------------------------------

# Run the Unit Test suite.
Comment thread
paulmedynski marked this conversation as resolved.
- 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

# Run the Functional Test suite.
- 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

# Run the Manual Test suite.
- 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
# ---------------------------------------------------------------------------

# Publish the TRX test results to the pipeline run.
- task: PublishTestResults@2
displayName: Publish Test Results
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: $(Build.SourcesDirectory)/test_results/**/*.trx
mergeTestResults: true
testRunTitle: ${{ parameters.testRunTitle }}
buildConfiguration: Release
Comment thread
paulmedynski marked this conversation as resolved.

# 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: |
Comment thread
paulmedynski marked this conversation as resolved.
cd $(Build.SourcesDirectory)/test_results
Get-ChildItem -Filter "*.coverage" -Recurse |
Rename-Item -NewName { "${{ parameters.testFramework }}" + $_.Name }
displayName: Rename coverage files
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(), eq(variables['HasTestResultsDir'], 'true'))
inputs:
targetPath: $(Build.SourcesDirectory)/test_results
artifact: ${{ parameters.artifactName }}
Comment thread
paulmedynski marked this conversation as resolved.
45 changes: 45 additions & 0 deletions eng/pipelines/kerberos/linux-cleanup-step.yml
Comment thread
paulmedynski marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -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
# linux-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()
117 changes: 117 additions & 0 deletions eng/pipelines/kerberos/linux-init-step.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
#################################################################################
# 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
# -----------------------------------------------------------------------
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)
# -----------------------------------------------------------------------
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

# -----------------------------------------------------------------------
# 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 ip addr
sudo ip route
displayName: Initialize Kerberos (domain join + kinit)
Loading
Loading