Skip to content

Pipelines | Move CI and PR jobs off Microsoft-hosted agents - #4515

Draft
cheenamalhotra wants to merge 4 commits into
dev/automation/ci-net10-coveragefrom
dev/automation/ci-retire-hosted-agents
Draft

Pipelines | Move CI and PR jobs off Microsoft-hosted agents#4515
cheenamalhotra wants to merge 4 commits into
dev/automation/ci-net10-coveragefrom
dev/automation/ci-retire-hosted-agents

Conversation

@cheenamalhotra

@cheenamalhotra cheenamalhotra commented Aug 7, 2026

Copy link
Copy Markdown
Member

Stacked PR 3 of 3. Targets dev/automation/ci-net10-coverage (#4514) - review #4513 and #4514 first.

Summary

Every job that previously ran on the Microsoft-hosted Azure Pipelines pool now runs on our 1ES pools. The sole exception is macOS, since our 1ES pools do not offer macOS images.

  • Parameterize the pool name and image for the secrets, pack, verify-nuget, and code-coverage jobs in both the CI and PR pipelines, defaulting to the 1ES pool with ADO-UB24-SQL25 (or ADO-MMS25-SQL25 for verify-nuget, which needs Windows).
  • Point the Abstractions package Linux and Windows test jobs at ADO-UB24-SQL25 and ADO-MMS25-SQL25.
  • Drop the redundant hosted Linux and Windows Azure package test jobs. The self-hosted integration jobs already cover the same runtimes, and additionally exercise a local SQL Server, so the hosted jobs were pure duplication. pack_azure_package_job dependencies were trimmed accordingly.
  • Pass explicit pool names from the stress and Kerberos pipelines. Those pipelines reuse these shared templates but import neither ci-build-variables.yml nor sqlclient-testconfig-v1, so the default pool variables are not defined there.
  • Move the CI package pipeline off ADO-UB24 / ADO-Win25 and onto ADO-UB24-SQL25 / ADO-MMS25-SQL25.
  • Move the GitHub sync pipeline off the hosted ubuntu-latest image and onto ADO-UB24-SQL25, using the same internal/public conditional pool convention as the other pipelines. pwsh is available natively on that image, so no install step is needed.
  • Support both pool types in every template that now takes poolName / vmImage. Selecting the image with a 1ES imageOverride demand unconditionally would break any caller still pointing at the hosted Azure Pipelines pool, which requires vmImage instead. These templates now use the same conditional pool block the test-* job templates already use.

Remaining hosted usage

Intentionally retained, all macOS:

  • dotnet-sqlclient-ci-core.yml - macOS test configuration
  • stages/build-azure-package-ci-stage.yml - macOS Azure package job
  • stages/build-abstractions-package-ci-stage.yml - macOS abstractions job
  • ci/stress/sqlclient-ci-stress-stage.yml - macOS stress job

There is no remaining non-macOS use of a Microsoft-hosted image in eng/pipelines.

Action required before merge

ADO-MMS25-SQL25 and ADO-UB24-SQL25 must be available in both ADO-1ES-Pool and ADO-CI-1ES-Pool, since the package and GitHub sync pipelines select the pool based on the project they run in.

Checklist

  • Tests added or updated - n/a, pipeline-only change
  • Public API changes documented - n/a
  • Verified against customer repro - n/a
  • Ensure no breaking changes introduced - no product code touched; note the two dropped duplicate Azure package jobs above

All modified YAML files parse cleanly. These changes have not yet been executed against Azure DevOps. Capacity planning note: this shifts load onto the 1ES pools, so agent allocation should be reviewed before merge.

Copilot AI lite review requested due to automatic review settings August 7, 2026 22:57
@cheenamalhotra
cheenamalhotra requested a review from a team as a code owner August 7, 2026 22:57
@github-project-automation github-project-automation Bot moved this to To triage in SqlClient Board Aug 7, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR completes the hosted-agent retirement work for the Azure DevOps CI/PR YAML by moving previously Microsoft-hosted jobs onto 1ES pools (except macOS), parameterizing pool/image selection across shared templates, and removing redundant hosted Azure package test jobs that duplicated integration coverage.

Changes:

  • Parameterized poolName/vmImage for multiple CI/PR stages and jobs, switching selection to 1ES-style imageOverride demands.
  • Removed redundant hosted Linux/Windows Azure package test jobs and trimmed downstream dependsOn accordingly.
  • Passed explicit pool names into stress and Kerberos pipelines that reuse templates without importing the default CI variable groups.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
eng/pipelines/stages/verify-nuget-packages-ci-stage.yml Parameterizes pool/image and moves verify job to 1ES pool selection.
eng/pipelines/stages/generate-secrets-ci-stage.yml Parameterizes pool/image and moves secrets job to 1ES pool selection.
eng/pipelines/stages/build-azure-package-ci-stage.yml Removes redundant hosted Azure test jobs; keeps macOS hosted; trims pack dependencies.
eng/pipelines/stages/build-abstractions-package-ci-stage.yml Routes Linux/Windows abstractions tests to 1ES images; keeps macOS hosted.
eng/pipelines/pr/stages/pack-stage.yml Parameterizes pool/image and moves PR pack stage onto 1ES pool selection.
eng/pipelines/pr/stages/generate-secrets-stage.yml Parameterizes pool/image and moves PR secrets stage onto 1ES pool selection.
eng/pipelines/pr/stages/collect-coverage-stage.yml Parameterizes pool/image and moves PR coverage stage onto 1ES pool selection.
eng/pipelines/jobs/pack-sqlserver-package-ci-job.yml Parameterizes pool/image and moves SqlServer pack job onto 1ES pool selection.
eng/pipelines/jobs/pack-logging-package-ci-job.yml Parameterizes pool/image and moves Logging pack job onto 1ES pool selection.
eng/pipelines/jobs/pack-azure-package-ci-job.yml Parameterizes pool/image and moves Azure pack job onto 1ES pool selection.
eng/pipelines/jobs/pack-abstractions-package-ci-job.yml Parameterizes pool/image and moves Abstractions pack job onto 1ES pool selection.
eng/pipelines/common/templates/jobs/ci-code-coverage-job.yml Parameterizes pool/image and moves coverage publish job onto 1ES pool selection.
eng/pipelines/ci/stress/sqlclient-ci-stress-pipeline.yml Explicitly passes poolName/vmImage for secrets stage (no CI vars imported).
eng/pipelines/ci/kerberos/sqlclient-ci-kerberos-pipeline.yml Explicitly passes poolName/vmImage for code coverage job (no CI vars imported).

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +69 to +72
pool:
name: Azure Pipelines
vmImage: windows-latest
name: ${{ parameters.poolName }}
demands:
- imageOverride -equals ${{ parameters.vmImage }}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 1aa857c3a. All ten of these templates now use the same conditional pool block the test-* job templates already use, so they work with either a 1ES pool (imageOverride demand) or the hosted Azure Pipelines pool (vmImage).

Comment on lines +59 to +63
# We don't need anything special, so any Linux image in our 1ES pool
# will do.
name: ${{ parameters.poolName }}
demands:
- imageOverride -equals ${{ parameters.vmImage }}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 1aa857c3a. All ten of these templates now use the same conditional pool block the test-* job templates already use, so they work with either a 1ES pool (imageOverride demand) or the hosted Azure Pipelines pool (vmImage).

Comment on lines +38 to +41
pool:
name: Azure Pipelines
vmImage: ubuntu-latest
name: ${{ parameters.poolName }}
demands:
- imageOverride -equals ${{ parameters.vmImage }}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 1aa857c3a. All ten of these templates now use the same conditional pool block the test-* job templates already use, so they work with either a 1ES pool (imageOverride demand) or the hosted Azure Pipelines pool (vmImage).

Comment on lines +71 to +74
pool:
name: Azure Pipelines
vmImage: ubuntu-latest
name: ${{ parameters.poolName }}
demands:
- imageOverride -equals ${{ parameters.vmImage }}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 1aa857c3a. All ten of these templates now use the same conditional pool block the test-* job templates already use, so they work with either a 1ES pool (imageOverride demand) or the hosted Azure Pipelines pool (vmImage).

Comment on lines +75 to +78
pool:
name: Azure Pipelines
vmImage: ubuntu-latest
name: ${{ parameters.poolName }}
demands:
- imageOverride -equals ${{ parameters.vmImage }}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 1aa857c3a. All ten of these templates now use the same conditional pool block the test-* job templates already use, so they work with either a 1ES pool (imageOverride demand) or the hosted Azure Pipelines pool (vmImage).

Comment on lines +111 to +114
pool:
name: Azure Pipelines
vmImage: ubuntu-latest
name: ${{ parameters.poolName }}
demands:
- imageOverride -equals ${{ parameters.vmImage }}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 1aa857c3a. All ten of these templates now use the same conditional pool block the test-* job templates already use, so they work with either a 1ES pool (imageOverride demand) or the hosted Azure Pipelines pool (vmImage).

Comment on lines +99 to +102
pool:
name: Azure Pipelines
vmImage: ubuntu-latest
name: ${{ parameters.poolName }}
demands:
- imageOverride -equals ${{ parameters.vmImage }}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 1aa857c3a. All ten of these templates now use the same conditional pool block the test-* job templates already use, so they work with either a 1ES pool (imageOverride demand) or the hosted Azure Pipelines pool (vmImage).

Comment thread eng/pipelines/pr/stages/pack-stage.yml Outdated
Comment on lines +65 to +68
pool:
vmImage: 'ubuntu-latest'
name: ${{ parameters.poolName }}
demands:
- imageOverride -equals ${{ parameters.vmImage }}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 1aa857c3a. All ten of these templates now use the same conditional pool block the test-* job templates already use, so they work with either a 1ES pool (imageOverride demand) or the hosted Azure Pipelines pool (vmImage).

Comment on lines +39 to +42
pool:
vmImage: ubuntu-latest
name: ${{ parameters.poolName }}
demands:
- imageOverride -equals ${{ parameters.vmImage }}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 1aa857c3a. All ten of these templates now use the same conditional pool block the test-* job templates already use, so they work with either a 1ES pool (imageOverride demand) or the hosted Azure Pipelines pool (vmImage).

Comment on lines +59 to +63
# We don't need anything special, so any Linux image in our 1ES pool
# will do.
name: ${{ parameters.poolName }}
demands:
- imageOverride -equals ${{ parameters.vmImage }}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 1aa857c3a. All ten of these templates now use the same conditional pool block the test-* job templates already use, so they work with either a 1ES pool (imageOverride demand) or the hosted Azure Pipelines pool (vmImage).

@cheenamalhotra
cheenamalhotra marked this pull request as draft August 7, 2026 23:18
Copilot AI review requested due to automatic review settings August 8, 2026 04:11
@cheenamalhotra
cheenamalhotra force-pushed the dev/automation/ci-retire-hosted-agents branch from b75fd7d to 8fb349a Compare August 8, 2026 04:13

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated no new comments.

Copilot AI review requested due to automatic review settings August 8, 2026 04:16

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated no new comments.

Suppressed comments (2)

eng/pipelines/stages/generate-secrets-ci-stage.yml:60

  • The comment says “any Linux image … will do”, but this job is actually pinned to a specific image via the imageOverride demand (defaulting to ADO-UB24-SQL25). This can confuse future edits (e.g., someone might remove/alter the demand thinking it’s unnecessary). Update the comment to clarify that the job only requires a Linux agent, but is still selecting a specific pool image via vmImage/imageOverride.
        pool:
          # We don't need anything special, so any Linux image in our 1ES pool
          # will do.

eng/pipelines/pr/stages/generate-secrets-stage.yml:60

  • The comment says “any Linux image … will do”, but this job is actually pinned to a specific image via the imageOverride demand (defaulting to ADO-UB24-SQL25). Update the comment to make it clear the job requires Linux but still selects an explicit pool image via vmImage/imageOverride, to avoid confusion when someone changes pool settings later.
        pool:
          # We don't need anything special, so any Linux image in our 1ES pool
          # will do.

Copilot AI review requested due to automatic review settings August 8, 2026 06:04

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated no new comments.

Suppressed comments (1)

eng/pipelines/github-sync-pipeline.yml:67

  • PR description states eng/pipelines/github-sync-pipeline.yml is "out of scope and untouched" and still uses a hosted image, but this PR changes the job pool to 1ES with an imageOverride demand. Please reconcile this by either updating the PR description (if the change is intended) or reverting this pool change (if it’s truly out of scope).
    # This pipeline only ever runs in the ADO.Net project, but the pool is
    # selected conditionally to match the convention used by the other
    # pipelines in this repo.
    pool:
      ${{ if eq(variables['System.TeamProject'], 'ADO.Net') }}:

cheenamalhotra and others added 4 commits August 7, 2026 23:48
All jobs that previously ran on the Microsoft-hosted 'Azure Pipelines'
pool now run on our 1ES pools, with the sole exception of macOS jobs,
since our 1ES pools do not offer macOS images.

  - Parameterize the pool name and image for the secrets, pack,
    verify-nuget, and code-coverage jobs in both the CI and PR
    pipelines, defaulting to the 1ES pool with a Linux image (or a
    Windows image, for verify-nuget).
  - Point the Abstractions package Linux and Windows test jobs at
    ADO-UB24-SQL25 and ADO-MMS25-SQL25.
  - Drop the redundant hosted Linux and Windows Azure package test jobs.
    The self-hosted integration jobs already cover the same runtimes,
    and additionally exercise a local SQL Server.
  - Pass explicit pool names from the stress and Kerberos pipelines,
    which reuse these templates but do not import the CI build
    variables.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 10246002-c950-42a7-adf5-1698f9af4b3d
The nightly package build pipeline still offered ADO-UB24 and ADO-Win25
as its agentImage choices.  Move it onto the same images the rest of CI
now uses: ADO-UB24-SQL25 and ADO-MMS25-SQL25.

The job only runs build.proj Pack, so it does not depend on the SQL
Server instance these images carry.  Aligning them means we maintain one
set of agent images rather than two.

Note that both ADO-1ES-Pool and ADO-CI-1ES-Pool must publish these
images before this merges, since the pool is chosen based on whether the
build is internal or public.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 10246002-c950-42a7-adf5-1698f9af4b3d
This was the last job outside of macOS still running on a
Microsoft-hosted agent.  Move it to ADO-UB24-SQL25 on the 1ES pool,
selecting the pool based on the project the way the stress and package
pipelines do.

The job runs a PowerShell script with pwsh, which the ADO-UB24-SQL25
image already provides -- the stress job relies on the same thing
without installing PowerShell first.

After this change, the only remaining Microsoft-hosted jobs are the
macOS ones, since our 1ES pools do not offer macOS images.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 10246002-c950-42a7-adf5-1698f9af4b3d
The pack, secrets, coverage and NuGet verification templates now take
poolName/vmImage parameters, but selected the image with a 1ES imageOverride
demand unconditionally.  That breaks if a caller (or a variable group that has
not been migrated yet) still points poolName at the hosted 'Azure Pipelines'
pool, which requires vmImage instead.

Use the same conditional pool block the test-* job templates already use, so
these templates work with either pool type.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 10246002-c950-42a7-adf5-1698f9af4b3d
Copilot AI review requested due to automatic review settings August 8, 2026 06:49
@cheenamalhotra
cheenamalhotra force-pushed the dev/automation/ci-retire-hosted-agents branch from 22e6b04 to 1aa857c Compare August 8, 2026 06:49
@cheenamalhotra

Copy link
Copy Markdown
Member Author

Also addressing the two suppressed comments from the earlier review passes:

  • generate-secrets-ci-stage.yml:60 / generate-secrets-stage.yml:60 - the "any Linux image in our 1ES pool will do" comments were misleading now that the job pins an image. Reworded in 1aa857c3a to state that the job has no special image requirements, defaults to Linux, and is repointable by the caller via the poolName/vmImage parameters.
  • github-sync-pipeline.yml:67 - correctly flagged as contradicting the PR description. The pool change is intended (it was the last non-macOS hosted image left in eng/pipelines), so I updated the PR description rather than reverting.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated no new comments.

Suppressed comments (1)

eng/pipelines/github-sync-pipeline.yml:67

  • PR description says eng/pipelines/github-sync-pipeline.yml is “out of scope and untouched” and still uses a hosted image, but this diff switches it to 1ES pools with an imageOverride demand. Please either update the PR description to include this pipeline in scope, or revert this file change to keep the stated “remaining hosted usage” accurate.
    # This pipeline only ever runs in the ADO.Net project, but the pool is
    # selected conditionally to match the convention used by the other
    # pipelines in this repo.
    pool:
      ${{ if eq(variables['System.TeamProject'], 'ADO.Net') }}:

@paulmedynski paulmedynski left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good step to having consistent pool and image selection for our pipline jobs, and getting us off the generic Azure Pipelines pool except for macOS.

I would like to see us using the minimal image wherever possible to save time and resources - i.e. only use an image with a SQL Server if we're running integration tests that need it. Jobs that build, pack, run unit/functional tests, or use a remote SQL Server don't need a local SQL Server. We also don't need the large MMS tooling (that includes Visual Studio and a bunch of other stuff) for most (all?) of our jobs. I realize that some of our SQL Server images are currently based on MMS, so for now we can't avoid it with the local integration jobs.

We currently have ADO-Win25 and ADO-UB24 in both 1ES pools, and those can be used for these legacy pipeline non-integration jobs. The nascent 1ES Pipeline Plan will have us creating new images to meet our new PR/CI pipeline needs, and we can use those in the new pipelines once they are created. I don't expect ADO-Win25 and ADO-UB24 to survive that transition.

I'm also wondering if we really need the pool and image names to be configurable. Do we plan to change either regularly? Would it be sufficient to put the pool name selection in a common template and expose it as a static variable that can be used as ${{ variables.poolName }} at template-expansion time? The image names are essentially hardcoded already anyway, so that's fine.

# This pipeline does not import the CI build variables, so the pool
# must be named explicitly. Reuse the Linux stage's pool and image.
poolName: ADO-Trusted-Linux-WestUS2
vmImage: ADO-UB20-SQL22

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use a minimal OS-only image, like ADO-UB24? Collecting code coverage shouldn't require a local SQL Server.

displayName: Agent Image
type: string
default: ADO-UB24
default: ADO-UB24-SQL25

@paulmedynski paulmedynski Aug 8, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These jobs don't need a local SQL Server or any extra tooling (like MMS brings). They only need a base OS and minimal tooling.

# The name of the VM image to run on, within the pool.
- name: vmImage
type: string
default: ADO-UB24-SQL25

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for local SQL Server - this can use ADO-UB24.

name: ${{ parameters.poolName }}

# Images provided by Azure Pipelines must be selected using 'vmImage'.
${{ if eq(parameters.poolName, 'Azure Pipelines') }}:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At template expansion time (when ${{ }} expressions are evaluated), the parameters.poolName value will be the literal string $(ci_var_defaultPoolName) if the default is used, in which case we will end up in the ${{ else }} block regardless of what the runtime-expanded value of $(ci_var_defaultPoolName) is. We can't use a library variable to choose images like this unfortunately.

Do we really need the flexibility to change pool and image names as pipeline configuration?

# The name of the VM image to run on, within the pool.
- name: vmImage
type: string
default: ADO-UB24-SQL25

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Building and packing doesn't require a local SQL Server.

@github-project-automation github-project-automation Bot moved this from To triage to Waiting for customer in SqlClient Board Aug 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Waiting for customer

Development

Successfully merging this pull request may close these issues.

3 participants