dotnet-ado-unique

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

dotnet-ado-unique

dotnet-ado-unique

Azure DevOps-exclusive features not available in GitHub Actions: Environments with approvals and gates (pre-deployment checks, business hours restrictions), deployment groups vs environments (when to use each), service connections (Azure Resource Manager, Docker Registry, NuGet), classic release pipelines (legacy migration guidance to YAML), variable groups and library (linked to Azure Key Vault), pipeline decorators for organization-wide policy, and Azure Artifacts universal packages.
Version assumptions: Azure DevOps Services (cloud). YAML pipelines with multi-stage support. Classic release pipelines for legacy migration context only.
Scope boundary: This skill owns ADO-exclusive platform features that have no direct GitHub Actions equivalent. Composable YAML pipeline patterns (templates, triggers, multi-stage) are in [skill:dotnet-ado-patterns]. Build/test pipeline configuration is in [skill:dotnet-ado-build-test]. Publishing pipelines are in [skill:dotnet-ado-publish]. Starter CI templates are owned by [skill:dotnet-add-ci].
Out of scope: Composable pipeline patterns (templates, triggers) -- see [skill:dotnet-ado-patterns]. Build/test pipeline configuration -- see [skill:dotnet-ado-build-test]. Publishing pipelines -- see [skill:dotnet-ado-publish]. Starter CI templates -- see [skill:dotnet-add-ci]. GitHub Actions equivalents -- see [skill:dotnet-gha-patterns], [skill:dotnet-gha-build-test], [skill:dotnet-gha-publish], [skill:dotnet-gha-deploy]. CLI release pipelines -- see [skill:dotnet-cli-release-pipeline].
Cross-references: [skill:dotnet-add-ci] for starter CI templates, [skill:dotnet-cli-release-pipeline] for CLI-specific release automation.

GitHub Actions中没有的Azure DevOps专属功能:带审批与闸门的环境(部署前检查、营业时间限制)、部署组与环境的对比(各自适用场景)、服务连接(Azure Resource Manager、Docker Registry、NuGet)、经典发布管道(向YAML迁移的指南)、与Azure Key Vault关联的变量组和库、用于组织级策略的管道装饰器,以及Azure Artifacts通用包。
版本说明:Azure DevOps Services(云端版本)。支持多阶段的YAML管道。经典发布管道仅用于遗留系统迁移场景。
范围界定:本技能涵盖无直接GitHub Actions等效功能的ADO专属平台特性。可组合的YAML管道模式(模板、触发器、多阶段)属于[skill:dotnet-ado-patterns]。构建/测试管道配置属于[skill:dotnet-ado-build-test]。发布管道属于[skill:dotnet-ado-publish]。入门级CI模板由[skill:dotnet-add-ci]负责。
超出范围:可组合管道模式(模板、触发器)——详见[skill:dotnet-ado-patterns]。构建/测试管道配置——详见[skill:dotnet-ado-build-test]。发布管道——详见[skill:dotnet-ado-publish]。入门级CI模板——详见[skill:dotnet-add-ci]。GitHub Actions等效功能——详见[skill:dotnet-gha-patterns]、[skill:dotnet-gha-build-test]、[skill:dotnet-gha-publish]、[skill:dotnet-gha-deploy]。CLI发布管道——详见[skill:dotnet-cli-release-pipeline]。
交叉引用:入门级CI模板详见[skill:dotnet-add-ci],CLI专属发布自动化详见[skill:dotnet-cli-release-pipeline]。

Environments with Approvals and Gates

带审批与闸门的环境

Defining Environments in YAML

在YAML中定义环境

Environments are first-class Azure DevOps resources that provide deployment targeting, approval gates, and deployment history:
yaml
stages:
  - stage: DeployStaging
    jobs:
      - deployment: DeployToStaging
        pool:
          vmImage: 'ubuntu-latest'
        environment: 'staging'
        strategy:
          runOnce:
            deploy:
              steps:
                - download: current
                  artifact: app
                - script: echo "Deploying to staging"

  - stage: DeployProduction
    dependsOn: DeployStaging
    jobs:
      - deployment: DeployToProduction
        pool:
          vmImage: 'ubuntu-latest'
        environment: 'production'
        strategy:
          runOnce:
            deploy:
              steps:
                - download: current
                  artifact: app
                - script: echo "Deploying to production"
Environments are created automatically on first reference. Configure approvals and gates in Azure DevOps > Pipelines > Environments > (select environment) > Approvals and checks.
环境是Azure DevOps的一等资源,可实现部署目标定位、审批闸门和部署历史追踪:
yaml
stages:
  - stage: DeployStaging
    jobs:
      - deployment: DeployToStaging
        pool:
          vmImage: 'ubuntu-latest'
        environment: 'staging'
        strategy:
          runOnce:
            deploy:
              steps:
                - download: current
                  artifact: app
                - script: echo "Deploying to staging"

  - stage: DeployProduction
    dependsOn: DeployStaging
    jobs:
      - deployment: DeployToProduction
        pool:
          vmImage: 'ubuntu-latest'
        environment: 'production'
        strategy:
          runOnce:
            deploy:
              steps:
                - download: current
                  artifact: app
                - script: echo "Deploying to production"
首次引用环境时会自动创建。可在Azure DevOps > 管道 > 环境 >(选择对应环境)> 审批与检查中配置审批和闸门。

Approval Checks

审批检查

Check TypePurposeConfiguration
ApprovalsManual sign-off before deploymentAssign approver users/groups
Branch controlRestrict deployments to specific branchesAllow only
main
,
release/*
Business hoursDeploy only during allowed time windowsDefine hours and timezone
Template validationRequire pipeline to extend a specific templateSpecify required template path
Invoke Azure FunctionCustom validation via Azure FunctionProvide function URL and key
Invoke REST APICustom validation via HTTP endpointProvide URL and success criteria
Required templateEnforce pipeline structureSpecify required extends template
检查类型用途配置方式
审批部署前需人工签字确认分配审批用户/组
分支控制限制仅特定分支可触发部署仅允许
main
release/*
等分支
营业时间仅在允许的时间段内部署定义时间窗口及时区
模板验证要求管道必须继承特定模板指定必填模板路径
调用Azure Function通过Azure Function实现自定义验证提供函数URL和密钥
调用REST API通过HTTP端点实现自定义验证提供URL和成功判定条件
必填模板强制管道结构规范指定必须继承的模板

Configuring Approval Checks

配置审批检查

Approval checks are configured in the Azure DevOps UI, not in YAML. The YAML pipeline references the environment, and the checks are applied:
yaml
undefined
审批检查需在Azure DevOps UI中配置,而非YAML。YAML管道仅需引用环境名称,检查规则会自动生效:
yaml
undefined

Pipeline YAML -- environment reference triggers checks

管道YAML——环境引用会触发检查

  • deployment: DeployToProduction environment: 'production' # checks configured in UI strategy: runOnce: deploy: steps: - script: echo "This runs only after all checks pass"

**Approval configuration (UI):**
- Navigate to Pipelines > Environments > production > Approvals and checks
- Add "Approvals" check: assign individuals or groups
- Set minimum number of approvers (e.g., 2 for production)
- Enable "allow approvers to approve their own runs" only if appropriate
  • deployment: DeployToProduction environment: 'production' # 检查规则在UI中配置 strategy: runOnce: deploy: steps: - script: echo "This runs only after all checks pass"

**审批配置(UI操作):**
- 导航至管道 > 环境 > production > 审批与检查
- 添加「审批」检查:指定个人或组作为审批者
- 设置最小审批人数(例如生产环境需2人)
- 仅在必要时启用「允许审批者批准自己触发的运行」

Business Hours Gate

营业时间闸门

Restrict deployments to specific time windows to reduce risk:
  • Navigate to Pipelines > Environments > production > Approvals and checks
  • Add "Business Hours" check
  • Configure: Monday-Friday, 09:00-17:00 (team timezone)
  • Pipelines will queue and wait until the window opens
将部署限制在特定时间窗口内以降低风险:
  • 导航至管道 > 环境 > production > 审批与检查
  • 添加「营业时间」检查
  • 配置:周一至周五,09:00-17:00(团队时区)
  • 管道将进入排队状态,直到时间窗口开启

Pre-Deployment Validation with Azure Functions

借助Azure Function实现部署前验证

yaml
undefined
yaml
undefined

The environment's "Invoke Azure Function" check calls:

环境的「调用Azure Function」检查会调用:

with the pipeline context as payload.

并将管道上下文作为请求体。

Returns 200 to approve, non-200 to reject.

返回200表示批准,非200表示拒绝。

  • deployment: DeployToProduction environment: 'production' # Azure Function check configured in UI strategy: runOnce: preDeploy: steps: - script: echo "Pre-deploy hook (in-pipeline)" deploy: steps: - script: echo "Deploying" routeTraffic: steps: - script: echo "Routing traffic" postRouteTraffic: steps: - script: echo "Post-route validation"

The `preDeploy`, `routeTraffic`, and `postRouteTraffic` lifecycle hooks execute within the pipeline. Environment checks (approvals, Azure Function gates) execute before the deployment job starts.

---
  • deployment: DeployToProduction environment: 'production' # Azure Function检查在UI中配置 strategy: runOnce: preDeploy: steps: - script: echo "Pre-deploy hook (in-pipeline)" deploy: steps: - script: echo "Deploying" routeTraffic: steps: - script: echo "Routing traffic" postRouteTraffic: steps: - script: echo "Post-route validation"

`preDeploy`、`routeTraffic`和`postRouteTraffic`生命周期钩子在管道内部执行。环境检查(审批、Azure Function闸门)则在部署作业启动前执行。

---

Deployment Groups vs Environments

部署组与环境的对比

When to Use Each

各自适用场景

FeatureDeployment GroupsEnvironments
TargetPhysical/virtual machines with agentsAny target (VMs, Kubernetes, cloud services)
Agent modelSelf-hosted agents on target machinesPool agents or target-specific resources
Pipeline typeClassic release pipelines (legacy)YAML multi-stage pipelines (modern)
ApprovalsPer-stage in classic UIChecks and approvals on environment
Rolling deploymentBuilt-in rolling strategy
strategy: rolling
in YAML
RecommendationLegacy workloads onlyAll new projects
特性部署组环境
目标对象安装了Agent的物理/虚拟机任意目标(虚拟机、Kubernetes、云服务)
Agent模式目标机器上的自托管Agent池化Agent或目标专属资源
管道类型经典发布管道(遗留)多阶段YAML管道(现代)
审批方式经典UI中按阶段配置环境层面的检查与审批
滚动部署内置滚动策略YAML中使用
strategy: rolling
推荐场景仅遗留工作负载所有新项目

Deployment Group Example (Legacy)

部署组示例(遗留)

Deployment groups install an agent on each target machine. Use only for existing on-premises deployments:
yaml
undefined
部署组会在每台目标机器上安装Agent。仅适用于现有本地部署场景:
yaml
undefined

Classic release pipeline (not YAML) -- for reference only

经典发布管道(非YAML)——仅作参考

Deployment groups are configured in Project Settings > Deployment Groups

部署组在项目设置 > 部署组中配置

Each target server runs the ADO agent registered to the group

每台目标服务器运行已注册到该组的ADO Agent

undefined
undefined

Environment with Kubernetes Resource

关联Kubernetes资源的环境

yaml
- deployment: DeployToK8s
  environment: 'production.my-k8s-namespace'
  strategy:
    runOnce:
      deploy:
        steps:
          - task: KubernetesManifest@1
            inputs:
              action: 'deploy'
              manifests: 'k8s/*.yml'
              containers: '$(ACR_LOGIN_SERVER)/myapp:$(Build.BuildId)'
Environments can target Kubernetes clusters and namespaces. Register the cluster as a resource under the environment in the Azure DevOps UI.
yaml
- deployment: DeployToK8s
  environment: 'production.my-k8s-namespace'
  strategy:
    runOnce:
      deploy:
        steps:
          - task: KubernetesManifest@1
            inputs:
              action: 'deploy'
              manifests: 'k8s/*.yml'
              containers: '$(ACR_LOGIN_SERVER)/myapp:$(Build.BuildId)'
环境可定位Kubernetes集群和命名空间。需在Azure DevOps UI中将集群注册为环境下的资源。

Migration from Deployment Groups to Environments

从部署组迁移到环境

  1. Create environments matching existing deployment group names
  2. Configure the same approval gates in the environment's Approvals and checks
  3. Convert classic release pipeline stages to YAML
    deployment
    jobs targeting the new environments
  4. Use
    strategy: rolling
    for incremental deployments equivalent to deployment group behavior

  1. 创建与现有部署组名称匹配的环境
  2. 在环境的审批与检查中配置相同的审批闸门
  3. 将经典发布管道阶段转换为指向新环境的YAML
    deployment
    作业
  4. 使用
    strategy: rolling
    实现与部署组等效的增量部署

Service Connections

服务连接

Azure Resource Manager (ARM)

Azure Resource Manager (ARM)

Service connections provide authenticated access to external services. ARM connections enable Azure resource deployments:
yaml
- task: AzureWebApp@1
  displayName: 'Deploy to Azure App Service'
  inputs:
    azureSubscription: 'MyAzureServiceConnection'
    appType: 'webAppLinux'
    appName: 'myapp-staging'
    package: '$(Pipeline.Workspace)/app'
Creating an ARM service connection:
  • Navigate to Project Settings > Service Connections > New service connection > Azure Resource Manager
  • Choose "Service principal (automatic)" for automatic credential management
  • Select the subscription and resource group scope
  • ADO creates an app registration and assigns Contributor role
服务连接提供对外部服务的认证访问。ARM连接可实现Azure资源部署:
yaml
- task: AzureWebApp@1
  displayName: 'Deploy to Azure App Service'
  inputs:
    azureSubscription: 'MyAzureServiceConnection'
    appType: 'webAppLinux'
    appName: 'myapp-staging'
    package: '$(Pipeline.Workspace)/app'
创建ARM服务连接:
  • 导航至项目设置 > 服务连接 > 新建服务连接 > Azure Resource Manager
  • 选择「服务主体(自动)」以实现凭证自动管理
  • 选择订阅和资源组范围
  • ADO会自动创建应用注册并分配参与者角色

Workload Identity Federation (Recommended)

工作负载身份联合(推荐)

Use workload identity federation for passwordless Azure authentication (no client secret):
  • Navigate to Project Settings > Service Connections > New service connection > Azure Resource Manager
  • Choose "Workload Identity federation (automatic)"
  • This creates a federated credential that trusts Azure DevOps pipeline tokens
  • No secret rotation required -- the credential uses short-lived pipeline tokens
使用工作负载身份联合实现无密码Azure认证(无需客户端密钥):
  • 导航至项目设置 > 服务连接 > 新建服务连接 > Azure Resource Manager
  • 选择「工作负载身份联合(自动)」
  • 系统会创建一个联合凭证,信任Azure DevOps管道令牌
  • 无需密钥轮换——凭证使用短期管道令牌

Docker Registry Service Connection

Docker Registry服务连接

yaml
- task: Docker@2
  displayName: 'Login to ACR'
  inputs:
    command: 'login'
    containerRegistry: 'MyACRServiceConnection'

- task: Docker@2
  displayName: 'Build and push'
  inputs:
    command: 'buildAndPush'
    containerRegistry: 'MyACRServiceConnection'
    repository: 'myapp'
    dockerfile: 'src/MyApp/Dockerfile'
Creating a Docker registry connection:
  • Project Settings > Service Connections > New service connection > Docker Registry
  • For ACR: select "Azure Container Registry" and choose the registry
  • For DockerHub: provide username and access token
yaml
- task: Docker@2
  displayName: 'Login to ACR'
  inputs:
    command: 'login'
    containerRegistry: 'MyACRServiceConnection'

- task: Docker@2
  displayName: 'Build and push'
  inputs:
    command: 'buildAndPush'
    containerRegistry: 'MyACRServiceConnection'
    repository: 'myapp'
    dockerfile: 'src/MyApp/Dockerfile'
创建Docker Registry连接:
  • 项目设置 > 服务连接 > 新建服务连接 > Docker Registry
  • 对于ACR:选择「Azure容器注册表」并选择对应注册表
  • 对于DockerHub:提供用户名和访问令牌

NuGet Service Connection

NuGet服务连接

For pushing to external NuGet feeds (e.g., nuget.org):
yaml
- task: NuGetCommand@2
  displayName: 'Push to nuget.org'
  inputs:
    command: 'push'
    packagesToPush: '$(Pipeline.Workspace)/nupkgs/*.nupkg'
    nuGetFeedType: 'external'
    publishFeedCredentials: 'NuGetOrgServiceConnection'
Creating a NuGet connection:
  • Project Settings > Service Connections > New service connection > NuGet
  • Provide the feed URL (
    https://api.nuget.org/v3/index.json
    ) and API key

用于推送包至外部NuGet源(如nuget.org):
yaml
- task: NuGetCommand@2
  displayName: 'Push to nuget.org'
  inputs:
    command: 'push'
    packagesToPush: '$(Pipeline.Workspace)/nupkgs/*.nupkg'
    nuGetFeedType: 'external'
    publishFeedCredentials: 'NuGetOrgServiceConnection'
创建NuGet连接:
  • 项目设置 > 服务连接 > 新建服务连接 > NuGet
  • 提供源URL(
    https://api.nuget.org/v3/index.json
    )和API密钥

Classic Release Pipelines (Legacy Migration)

经典发布管道(遗留系统迁移)

Why Migrate to YAML

为何迁移至YAML

Classic release pipelines use a visual designer and are not stored in source control. Migrate to YAML multi-stage pipelines for:
  • Source control: Pipeline definitions live alongside code
  • Code review: Pipeline changes go through PR review
  • Branch-specific pipelines: YAML pipelines can vary by branch
  • Reusability: Templates and extends for composable pipelines
  • Modern features: Environments, deployment strategies, pipeline decorators
经典发布管道使用可视化设计器,且定义不存储在源代码控制中。迁移至多阶段YAML管道可获得以下优势:
  • 源代码控制:管道定义与代码存储在一起
  • 代码评审:管道变更需经过PR评审
  • 分支专属管道:YAML管道可根据分支自定义
  • 可复用性:通过模板和继承实现可组合管道
  • 现代特性:环境、部署策略、管道装饰器

Migration Pattern

迁移模式

Classic release structure:
Build Pipeline -> Release Pipeline
                    Stage 1: Dev (auto-deploy)
                    Stage 2: Staging (manual approval)
                    Stage 3: Production (scheduled + approval)
Equivalent YAML multi-stage pipeline:
yaml
trigger:
  branches:
    include:
      - main

stages:
  - stage: Build
    jobs:
      - job: BuildJob
        pool:
          vmImage: 'ubuntu-latest'
        steps:
          - task: DotNetCoreCLI@2
            inputs:
              command: 'publish'
              projects: 'src/MyApp/MyApp.csproj'
              arguments: '-c Release -o $(Build.ArtifactStagingDirectory)/app'
          - task: PublishPipelineArtifact@1
            inputs:
              targetPath: '$(Build.ArtifactStagingDirectory)/app'
              artifactName: 'app'

  - stage: DeployDev
    dependsOn: Build
    jobs:
      - deployment: DeployDev
        environment: 'development'
        strategy:
          runOnce:
            deploy:
              steps:
                - download: current
                  artifact: app
                - script: echo "Deploy to dev"

  - stage: DeployStaging
    dependsOn: DeployDev
    jobs:
      - deployment: DeployStaging
        environment: 'staging'  # approvals configured in UI
        strategy:
          runOnce:
            deploy:
              steps:
                - download: current
                  artifact: app
                - script: echo "Deploy to staging"

  - stage: DeployProduction
    dependsOn: DeployStaging
    condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
    jobs:
      - deployment: DeployProduction
        environment: 'production'  # approvals + business hours in UI
        strategy:
          runOnce:
            deploy:
              steps:
                - download: current
                  artifact: app
                - script: echo "Deploy to production"
经典发布结构:
构建管道 -> 发布管道
                    阶段1:Dev(自动部署)
                    阶段2:Staging(人工审批)
                    阶段3:Production(定时触发+审批)
等效的多阶段YAML管道:
yaml
trigger:
  branches:
    include:
      - main

stages:
  - stage: Build
    jobs:
      - job: BuildJob
        pool:
          vmImage: 'ubuntu-latest'
        steps:
          - task: DotNetCoreCLI@2
            inputs:
              command: 'publish'
              projects: 'src/MyApp/MyApp.csproj'
              arguments: '-c Release -o $(Build.ArtifactStagingDirectory)/app'
          - task: PublishPipelineArtifact@1
            inputs:
              targetPath: '$(Build.ArtifactStagingDirectory)/app'
              artifactName: 'app'

  - stage: DeployDev
    dependsOn: Build
    jobs:
      - deployment: DeployDev
        environment: 'development'
        strategy:
          runOnce:
            deploy:
              steps:
                - download: current
                  artifact: app
                - script: echo "Deploy to dev"

  - stage: DeployStaging
    dependsOn: DeployDev
    jobs:
      - deployment: DeployStaging
        environment: 'staging'  # 审批在UI中配置
        strategy:
          runOnce:
            deploy:
              steps:
                - download: current
                  artifact: app
                - script: echo "Deploy to staging"

  - stage: DeployProduction
    dependsOn: DeployStaging
    condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
    jobs:
      - deployment: DeployProduction
        environment: 'production'  # 审批+营业时间限制在UI中配置
        strategy:
          runOnce:
            deploy:
              steps:
                - download: current
                  artifact: app
                - script: echo "Deploy to production"

Migration Checklist

迁移检查清单

  1. Identify all classic release stages and map to YAML stages
  2. Convert environment variables to YAML variable groups or templates
  3. Replace classic approval gates with environment checks
  4. Convert artifact sources to
    download: current
    or pipeline resources
  5. Replace task groups with YAML step or job templates
  6. Test the YAML pipeline on a non-production branch before decommissioning the classic release

  1. 识别所有经典发布阶段并映射为YAML阶段
  2. 转换环境变量为YAML变量组或模板
  3. 替换经典审批闸门为环境检查
  4. 转换制品源
    download: current
    或管道资源
  5. 替换任务组为YAML步骤或作业模板
  6. 在非生产分支测试YAML管道,之后再停用经典发布管道

Variable Groups and Library

变量组与库

Variable Groups Linked to Azure Key Vault

关联Azure Key Vault的变量组

Variable groups can pull secrets directly from Azure Key Vault at pipeline runtime:
yaml
variables:
  - group: 'kv-production-secrets'
  - group: 'build-settings'
  - name: buildConfiguration
    value: 'Release'

steps:
  - script: |
      echo "Building with configuration $(buildConfiguration)"
    displayName: 'Build'
    env:
      SQL_CONNECTION: $(sql-connection-string)  # from Key Vault
      API_KEY: $(api-key)                        # from Key Vault
Setting up Key Vault-linked variable groups:
  1. Navigate to Pipelines > Library > Variable Groups > New variable group
  2. Enable "Link secrets from an Azure key vault as variables"
  3. Select the Azure subscription (service connection) and Key Vault
  4. Choose which secrets to include
  5. Secrets are fetched at pipeline runtime and available as
    $(secret-name)
变量组可在管道运行时直接从Azure Key Vault拉取机密:
yaml
variables:
  - group: 'kv-production-secrets'
  - group: 'build-settings'
  - name: buildConfiguration
    value: 'Release'

steps:
  - script: |
      echo "Building with configuration $(buildConfiguration)"
    displayName: 'Build'
    env:
      SQL_CONNECTION: $(sql-connection-string)  # 来自Key Vault
      API_KEY: $(api-key)                        # 来自Key Vault
配置Key Vault关联变量组:
  1. 导航至管道 > 库 > 变量组 > 新建变量组
  2. 启用「将Azure密钥保管库中的机密链接为变量」
  3. 选择Azure订阅(服务连接)和Key Vault
  4. 选择需要包含的机密
  5. 机密会在管道运行时拉取,可通过
    $(secret-name)
    引用

Scoping Variable Groups to Environments

按环境作用域划分变量组

Use conditional variable group references based on pipeline stage:
yaml
stages:
  - stage: DeployStaging
    variables:
      - group: 'staging-config'
      - group: 'kv-staging-secrets'
    jobs:
      - deployment: Deploy
        environment: 'staging'
        strategy:
          runOnce:
            deploy:
              steps:
                - script: echo "Deploying with staging config"
                  env:
                    CONNECTION_STRING: $(sql-connection-string)

  - stage: DeployProduction
    variables:
      - group: 'production-config'
      - group: 'kv-production-secrets'
    jobs:
      - deployment: Deploy
        environment: 'production'
        strategy:
          runOnce:
            deploy:
              steps:
                - script: echo "Deploying with production config"
                  env:
                    CONNECTION_STRING: $(sql-connection-string)
可根据管道阶段条件引用不同变量组:
yaml
stages:
  - stage: DeployStaging
    variables:
      - group: 'staging-config'
      - group: 'kv-staging-secrets'
    jobs:
      - deployment: Deploy
        environment: 'staging'
        strategy:
          runOnce:
            deploy:
              steps:
                - script: echo "Deploying with staging config"
                  env:
                    CONNECTION_STRING: $(sql-connection-string)

  - stage: DeployProduction
    variables:
      - group: 'production-config'
      - group: 'kv-production-secrets'
    jobs:
      - deployment: Deploy
        environment: 'production'
        strategy:
          runOnce:
            deploy:
              steps:
                - script: echo "Deploying with production config"
                  env:
                    CONNECTION_STRING: $(sql-connection-string)

Secure Files in Library

库中的安全文件

Store certificates, SSH keys, and other binary secrets in the Pipelines Library:
yaml
- task: DownloadSecureFile@1
  displayName: 'Download signing certificate'
  name: signingCert
  inputs:
    secureFile: 'code-signing.pfx'

- script: |
    dotnet nuget sign ./nupkgs/*.nupkg \
      --certificate-path $(signingCert.secureFilePath) \
      --certificate-password $(CERT_PASSWORD) \
      --timestamper http://timestamp.digicert.com
  displayName: 'Sign NuGet packages'

可在管道库中存储证书、SSH密钥和其他二进制机密:
yaml
- task: DownloadSecureFile@1
  displayName: 'Download signing certificate'
  name: signingCert
  inputs:
    secureFile: 'code-signing.pfx'

- script: |
    dotnet nuget sign ./nupkgs/*.nupkg \
      --certificate-path $(signingCert.secureFilePath) \
      --certificate-password $(CERT_PASSWORD) \
      --timestamper http://timestamp.digicert.com
  displayName: 'Sign NuGet packages'

Pipeline Decorators

管道装饰器

Pipeline decorators inject steps into every pipeline in an organization or project without modifying individual pipeline files. They enforce organizational policies:
管道装饰器可在无需修改单个管道文件的情况下,将步骤注入组织或项目中的所有管道,用于强制实施组织级策略:

Decorator Use Cases

装饰器适用场景

Use CaseImplementation
Mandatory security scanningInject credential scanner before every job
Compliance audit loggingInject telemetry step after every job
Required code analysisInject SonarQube analysis on main branch builds
License complianceInject dependency license scanner
场景实现方式
强制安全扫描在每个作业前注入凭证扫描步骤
合规审计日志在每个作业后注入遥测步骤
强制代码分析在主分支构建时注入SonarQube分析
许可证合规注入依赖许可证扫描步骤

Decorator Definition

装饰器定义

Decorators are packaged as Azure DevOps extensions:
yaml
undefined
装饰器需打包为Azure DevOps扩展:
yaml
undefined

vss-extension.json (extension manifest)

vss-extension.json(扩展清单)

{ "contributions": [ { "id": "required-security-scan", "type": "ms.azure-pipelines.pipeline-decorator", "targets": ["ms.azure-pipelines-agent-job"], "properties": { "template": "decorator.yml", "targetsExecutionOrder": "PreJob" } } ] }

```yaml
{ "contributions": [ { "id": "required-security-scan", "type": "ms.azure-pipelines.pipeline-decorator", "targets": ["ms.azure-pipelines-agent-job"], "properties": { "template": "decorator.yml", "targetsExecutionOrder": "PreJob" } } ] }

```yaml

decorator.yml

decorator.yml

steps:
  • task: CredentialScanner@1 displayName: '[Policy] Credential scan' condition: always()
undefined
steps:
  • task: CredentialScanner@1 displayName: '[Policy] Credential scan' condition: always()
undefined

Deployment Limitations

部署限制

  • Decorators require Azure DevOps organization admin permissions to install
  • They apply to all pipelines in the organization (or selected projects)
  • Pipeline authors cannot override or skip decorator steps
  • Decorator steps run under the pipeline's agent pool and service connection context

  • 装饰器需要Azure DevOps组织管理员权限才能安装
  • 会应用于组织内所有管道(或选定项目)
  • 管道作者无法覆盖或跳过装饰器步骤
  • 装饰器步骤在管道的Agent池和服务连接上下文中运行

Azure Artifacts Universal Packages

Azure Artifacts通用包

Universal packages store arbitrary files (binaries, tools, datasets) in Azure Artifacts feeds, not limited to NuGet/npm/Maven formats:
通用包可在Azure Artifacts源中存储任意文件(二进制文件、工具、数据集),不限于NuGet/npm/Maven格式:

Publish a Universal Package

发布通用包

yaml
- task: UniversalPackages@0
  displayName: 'Publish universal package'
  inputs:
    command: 'publish'
    publishDirectory: '$(Build.ArtifactStagingDirectory)/tools'
    feedsToUsePublish: 'internal'
    vstsFeedPublish: 'MyProject/MyFeed'
    vstsFeedPackagePublish: 'my-dotnet-tool'
    versionOption: 'custom'
    versionPublish: '$(Build.BuildNumber)'
    packagePublishDescription: '.NET CLI tool binaries'
yaml
- task: UniversalPackages@0
  displayName: 'Publish universal package'
  inputs:
    command: 'publish'
    publishDirectory: '$(Build.ArtifactStagingDirectory)/tools'
    feedsToUsePublish: 'internal'
    vstsFeedPublish: 'MyProject/MyFeed'
    vstsFeedPackagePublish: 'my-dotnet-tool'
    versionOption: 'custom'
    versionPublish: '$(Build.BuildNumber)'
    packagePublishDescription: '.NET CLI tool binaries'

Download a Universal Package

下载通用包

yaml
- task: UniversalPackages@0
  displayName: 'Download universal package'
  inputs:
    command: 'download'
    feedsToUse: 'internal'
    vstsFeed: 'MyProject/MyFeed'
    vstsFeedPackage: 'my-dotnet-tool'
    vstsPackageVersion: '*'
    downloadDirectory: '$(Pipeline.Workspace)/tools'
yaml
- task: UniversalPackages@0
  displayName: 'Download universal package'
  inputs:
    command: 'download'
    feedsToUse: 'internal'
    vstsFeed: 'MyProject/MyFeed'
    vstsFeedPackage: 'my-dotnet-tool'
    vstsPackageVersion: '*'
    downloadDirectory: '$(Pipeline.Workspace)/tools'

Use Cases for .NET Projects

.NET项目适用场景

  • CLI tool distribution: Publish self-contained .NET CLI tool binaries for cross-team consumption
  • Build tool caching: Store custom MSBuild tasks or analyzers used across repositories
  • Test fixture data: Publish large test datasets that should not be stored in Git
  • AOT binaries: Distribute pre-built Native AOT binaries for platforms where on-demand compilation is impractical

  • CLI工具分发:发布自包含的.NET CLI工具二进制文件供跨团队使用
  • 构建工具缓存:存储跨仓库使用的自定义MSBuild任务或分析器
  • 测试数据集:发布不适合存储在Git中的大型测试数据
  • AOT二进制文件:分发预编译的Native AOT二进制文件,适用于无法按需编译的平台

Agent Gotchas

Agent注意事项

  1. Environment checks (approvals, gates) are configured in the UI, not YAML -- the YAML pipeline references the environment name; all checks are managed through the Azure DevOps web UI.
  2. Deployment groups are legacy -- use environments for all new projects; deployment groups exist only for backward compatibility with classic release pipelines.
  3. Service connection scope matters -- ARM connections scoped to a resource group cannot deploy to resources outside that group; use subscription-level scope for cross-resource-group deployments.
  4. Workload identity federation is preferred over service principal secrets -- federated credentials eliminate secret rotation; use automatic federation for new connections.
  5. Key Vault-linked variable groups fetch secrets at runtime -- template expressions (
    ${{ }}
    ) cannot access Key Vault secrets because they resolve at compile time; use runtime expressions (
    $()
    ) instead.
  6. Classic release pipelines are not stored in source control -- this is a primary motivation for migration; YAML pipelines enable PR review and branch-specific definitions.
  7. Pipeline decorators cannot be bypassed by pipeline authors -- this is intentional for policy enforcement; test decorator changes in a separate organization or project to avoid breaking all pipelines.
  8. Universal packages have a 4 GiB size limit per file -- for larger artifacts, split files or use Azure Blob Storage with a SAS token instead.
  1. 环境检查(审批、闸门)需在UI中配置,而非YAML——YAML管道仅引用环境名称;所有检查规则通过Azure DevOps网页UI管理。
  2. 部署组属于遗留功能——所有新项目应使用环境;部署组仅为兼容经典发布管道而保留。
  3. 服务连接的作用域很重要——限定为资源组的ARM连接无法部署到组外资源;跨资源组部署需使用订阅级作用域。
  4. 工作负载身份联合优于服务主体密钥——联合凭证无需密钥轮换;新建连接时优先使用自动联合方式。
  5. 关联Key Vault的变量组在运行时拉取机密——模板表达式(
    ${{ }}
    )无法访问Key Vault机密(因为在编译时解析);需使用运行时表达式(
    $()
    )。
  6. 经典发布管道不存储在源代码控制中——这是迁移的主要原因;YAML管道支持PR评审和分支专属定义。
  7. 管道装饰器无法被管道作者绕过——这是为了确保策略强制实施;需在单独的组织或项目中测试装饰器变更,避免影响所有管道。
  8. 通用包单文件大小限制为4 GiB——对于更大的制品,可拆分文件或使用带SAS令牌的Azure Blob存储。