mcpField
D
dotnet-ado-unique

dotnet-ado-unique

github.com/wshaddix云与 DevOps8667 次安装35 周前更新
代码仓库

关于 dotnet-ado-unique

为使用 Azure DevOps Services 的 .NET 团队提供平台专属流水线指导,涵盖部署门禁、服务连接、变量组和经典发布流水线迁移。

主要功能

  1. 01环境审批与检查
  2. 02部署组和环境的选择与迁移
  3. 03Azure、Docker Registry 和 NuGet 服务连接
  4. 04经典发布流水线迁移到 YAML
  5. 05Key Vault 变量组与安全文件
  6. 06组织级流水线策略装饰器

适用场景

  1. 01为生产部署配置审批和业务时段限制
  2. 02将部署组工作负载迁移到环境
  3. 03配置 Azure、容器仓库或 NuGet 认证
  4. 04将经典发布定义改造为多阶段 YAML

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/wshaddix/dotnet-skills --skill dotnet-ado-unique

安装后在 Claude Code 中输入/dotnet-ado-unique确认已加载。

详细介绍

dotnet-ado-unique

Azure DevOps 独有、GitHub Actions 不具备的功能:包含审批和门禁的环境(部署前检查、工作时间限制)、部署组与环境(何时使用哪一种)、服务连接(Azure Resource Manager、Docker Registry、NuGet)、经典发布管道(迁移到 YAML 的旧版指导)、变量组和库(关联到 Azure Key Vault)、用于组织级策略的管道装饰器,以及 Azure Artifacts 通用包。

版本假设: Azure DevOps Services(云)。支持多阶段的 YAML 管道。仅将经典发布管道用于旧版迁移场景。

范围边界: 此技能负责 ADO 独有且没有直接 GitHub Actions 对应项的平台功能。可组合的 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]。

交叉引用:[skill:dotnet-add-ci] 中的入门 CI 模板,以及 [skill:dotnet-cli-release-pipeline] 中的 CLI 专属发布自动化。


包含审批和门禁的环境

在 YAML 中定义环境

环境是 Azure DevOps 的一等资源,可提供部署目标、审批门禁和部署历史记录:

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 > Pipelines > Environments >(选择环境)> Approvals and checks 中配置审批和门禁。

审批检查
检查类型用途配置
审批部署前手动批准指定审批用户/组
分支控制将部署限制为特定分支仅允许 main、release/*
工作时间仅在允许的时间段内部署定义时间段和时区
模板验证要求管道扩展特定模板指定所需模板路径
调用 Azure Function通过 Azure Function 进行自定义验证提供函数 URL 和密钥
调用 REST API通过 HTTP 端点进行自定义验证提供 URL 和成功条件
必需模板强制执行管道结构指定必需的 extends 模板
配置审批检查

审批检查在 Azure DevOps UI 中配置,而不是在 YAML 中配置。YAML 管道引用环境,检查则会应用于该环境:

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

审批配置(UI):

  • 转到 Pipelines > Environments > production > Approvals and checks
  • 添加“Approvals”检查:指定个人或组
  • 设置最低审批人数(例如,生产环境需 2 人)
  • 仅在适用时启用“允许审批人批准自己的运行”
工作时间门禁

将部署限制在特定时间段内,以降低风险:

  • 转到 Pipelines > Environments > production > Approvals and checks
  • 添加“Business Hours”检查
  • 配置:星期一至星期五,09:00-17:00(团队时区)
  • 管道将排队等待,直到开放时段开始
使用 Azure Functions 进行部署前验证
# The environment's "Invoke Azure Function" check calls:
# https://myvalidation.azurewebsites.net/api/pre-deploy
# with the pipeline context as payload.
# Returns 200 to approve, non-200 to reject.

- 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"

preDeploy、routeTraffic 和 postRouteTraffic 生命周期挂钩在管道中执行。环境检查(审批、Azure Function 门禁)则在部署作业开始前执行。


部署组与环境

何时使用各项功能
功能部署组环境
目标安装了 Agent 的物理机/虚拟机任意目标(VM、Kubernetes、云服务)
Agent 模型目标计算机上的自托管 AgentAgent 池或特定于目标的资源
管道类型经典发布管道(旧版)YAML 多阶段管道(现代)
审批经典 UI 中按阶段设置在环境上设置检查和审批
滚动部署内置滚动策略YAML 中的 strategy: rolling
建议仅用于旧版工作负载所有新项目
部署组示例(旧版)

部署组会在每台目标计算机上安装 Agent。仅用于现有的本地部署:

# Classic release pipeline (not YAML) -- for reference only
# Deployment groups are configured in Project Settings > Deployment Groups
# Each target server runs the ADO agent registered to the group
使用 Kubernetes 资源的环境
- 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 中将集群注册为环境下的资源。

从部署组迁移到环境
  1. 创建与现有部署组名称对应的环境
  2. 在环境的“审批和检查”中配置相同的审批门禁
  3. 将经典发布管道阶段转换为指向新环境的 YAML deployment 作业
  4. 使用 strategy: rolling 实现与部署组行为相当的增量部署

服务连接

Azure Resource Manager(ARM)

服务连接可提供经过身份验证的外部服务访问权限。ARM 连接支持部署 Azure 资源:

- 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 会创建应用注册并分配“参与者”角色
工作负载标识联合(推荐)

使用工作负载标识联合进行 Azure 无密码身份验证(无需客户端机密):

  • 前往“项目设置”>“服务连接”>“新建服务连接”>“Azure Resource Manager”
  • 选择“工作负载标识联合(自动)”
  • 这会创建一个联合凭据,用于信任 Azure DevOps 管道令牌
  • 无需轮换机密——凭据使用有效期短的管道令牌
Docker 注册表服务连接
- 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 注册表连接:

  • “项目设置”>“服务连接”>“新建服务连接”>“Docker 注册表”
  • 对于 ACR:选择“Azure Container Registry”并选择注册表
  • 对于 DockerHub:提供用户名和访问令牌
NuGet 服务连接

若要推送到外部 NuGet 源(例如 nuget.org):

- 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 密钥

经典发布管道(旧版迁移)

为什么要迁移到 YAML

经典发布管道使用可视化设计器,且不会存储在源代码管理中。迁移到 YAML 多阶段管道的原因:

  • 源代码管理: 管道定义与代码一同存放
  • 代码评审: 管道变更通过 PR 评审
  • 分支专属管道: YAML 管道可以因分支而异
  • 可复用性: 使用模板和 extends 构建可组合的管道
  • 现代功能: 环境、部署策略、管道装饰器
迁移模式

经典发布结构:

Build Pipeline -> Release Pipeline
                    Stage 1: Dev (auto-deploy)
                    Stage 2: Staging (manual approval)
                    Stage 3: Production (scheduled + approval)

等效的 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. 识别所有经典发布阶段,并映射到 YAML 阶段
  2. 将环境变量转换为 YAML 变量组或模板
  3. 将经典审批关卡替换为环境检查
  4. 将工件源转换为 download: current 或管道资源
  5. 将任务组替换为 YAML 步骤或作业模板
  6. 在弃用经典发布之前,先在非生产分支上测试 YAML 管道

变量组和库

链接到 Azure Key Vault 的变量组

变量组可以在管道运行时直接从 Azure Key Vault 提取机密:

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

设置链接到 Key Vault 的变量组:

  1. 导航到 Pipelines > Library > Variable Groups > New variable group
  2. 启用“Link secrets from an Azure key vault as variables”
  3. 选择 Azure 订阅(服务连接)和 Key Vault
  4. 选择要包含的机密
  5. 机密会在管道运行时获取,并可通过 $(secret-name) 使用
将变量组限定到环境

根据管道阶段使用条件变量组引用:

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)
库中的安全文件

在 Pipelines Library 中存储证书、SSH 密钥和其他二进制机密:

- 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'

管道装饰器

管道装饰器无需修改单个管道文件,即可将步骤注入组织或项目中的每个管道。它们用于强制执行组织策略:

装饰器使用场景
使用场景实现方式
强制安全扫描在每个作业前注入凭据扫描器
合规审计日志记录在每个作业后注入遥测步骤
必需的代码分析在主分支构建中注入 SonarQube 分析
许可证合规注入依赖项许可证扫描器
装饰器定义

装饰器以 Azure DevOps 扩展的形式打包:

# vss-extension.json (extension manifest)
{
  "contributions": [
    {
      "id": "required-security-scan",
      "type": "ms.azure-pipelines.pipeline-decorator",
      "targets": ["ms.azure-pipelines-agent-job"],
      "properties": {
        "template": "decorator.yml",
        "targetsExecutionOrder": "PreJob"
      }
    }
  ]
}
# decorator.yml
steps:
  - task: CredentialScanner@1
    displayName: '[Policy] Credential scan'
    condition: always()
部署限制
  • 安装装饰器需要 Azure DevOps 组织管理员权限
  • 装饰器适用于组织中的所有管道(或所选项目)
  • 管道作者无法覆盖或跳过装饰器步骤
  • 装饰器步骤在管道的 Agent 池和服务连接上下文中运行

Azure Artifacts Universal Packages

Universal Packages 在 Azure Artifacts 源中存储任意文件(二进制文件、工具、数据集),不限于 NuGet/npm/Maven 格式:

发布 Universal Package
- 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'
下载 Universal Package
- task: UniversalPackages@0
  displayName: 'Download universal package'
  inputs:
    command: 'download'
    feedsToUse: 'internal'
    vstsFeed: 'MyProject/MyFeed'
    vstsFeedPackage: 'my-dotnet-tool'
    vstsPackageVersion: '*'
    downloadDirectory: '$(Pipeline.Workspace)/tools'
.NET 项目的使用场景
  • **CLI 工具分发:**发布可供跨团队使用的自包含 .NET CLI 工具二进制文件
  • **构建工具缓存:**存储多个仓库共用的自定义 MSBuild 任务或分析器
  • **测试夹具数据:**发布不应存储在 Git 中的大型测试数据集
  • **AOT 二进制文件:**为不适合按需编译的平台分发预构建的 Native AOT 二进制文件

Agent 注意事项

  1. 环境检查(审批、门禁)在 UI 中配置,而非 YAML 中——YAML 管道引用环境名称;所有检查都通过 Azure DevOps Web UI 管理。
  2. 部署组已过时——所有新项目都应使用环境;部署组仅为兼容经典发布管道而保留。
  3. 服务连接的作用域很重要——作用域限定为资源组的 ARM 连接无法部署到该组以外的资源;跨资源组部署时,请使用订阅级作用域。
  4. 工作负载标识联合优于服务主体密钥——联合凭据无需轮换密钥;新建连接时请使用自动联合。
  5. 链接到 Key Vault 的变量组会在运行时获取密钥——模板表达式(${{ }})无法访问 Key Vault 密钥,因为它们会在编译时解析;请改用运行时表达式($())。
  6. 经典发布管道不会存储在源代码管理中——这是迁移的主要动机;YAML 管道支持 PR 审查和特定分支的定义。
  7. 管道装饰器无法被管道作者绕过——这是为了强制执行策略;请在单独的组织或项目中测试装饰器更改,以免破坏所有管道。
  8. 通用包的单个文件大小上限为 4 GiB——对于更大的工件,请拆分文件,或改用带有 SAS 令牌的 Azure Blob Storage。

评论

支持 Markdown