关于 dotnet-ado-unique
为使用 Azure DevOps Services 的 .NET 团队提供平台专属流水线指导,涵盖部署门禁、服务连接、变量组和经典发布流水线迁移。
主要功能
- 01环境审批与检查
- 02部署组和环境的选择与迁移
- 03Azure、Docker Registry 和 NuGet 服务连接
- 04经典发布流水线迁移到 YAML
- 05Key Vault 变量组与安全文件
- 06组织级流水线策略装饰器
适用场景
- 01为生产部署配置审批和业务时段限制
- 02将部署组工作负载迁移到环境
- 03配置 Azure、容器仓库或 NuGet 认证
- 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 模型 | 目标计算机上的自托管 Agent | Agent 池或特定于目标的资源 |
| 管道类型 | 经典发布管道(旧版) | 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 中将集群注册为环境下的资源。
从部署组迁移到环境
- 创建与现有部署组名称对应的环境
- 在环境的“审批和检查”中配置相同的审批门禁
- 将经典发布管道阶段转换为指向新环境的 YAML
deployment作业 - 使用
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"
迁移检查清单
- 识别所有经典发布阶段,并映射到 YAML 阶段
- 将环境变量转换为 YAML 变量组或模板
- 将经典审批关卡替换为环境检查
- 将工件源转换为
download: current或管道资源 - 将任务组替换为 YAML 步骤或作业模板
- 在弃用经典发布之前,先在非生产分支上测试 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 的变量组:
- 导航到 Pipelines > Library > Variable Groups > New variable group
- 启用“Link secrets from an Azure key vault as variables”
- 选择 Azure 订阅(服务连接)和 Key Vault
- 选择要包含的机密
- 机密会在管道运行时获取,并可通过
$(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 注意事项
- 环境检查(审批、门禁)在 UI 中配置,而非 YAML 中——YAML 管道引用环境名称;所有检查都通过 Azure DevOps Web UI 管理。
- 部署组已过时——所有新项目都应使用环境;部署组仅为兼容经典发布管道而保留。
- 服务连接的作用域很重要——作用域限定为资源组的 ARM 连接无法部署到该组以外的资源;跨资源组部署时,请使用订阅级作用域。
- 工作负载标识联合优于服务主体密钥——联合凭据无需轮换密钥;新建连接时请使用自动联合。
- 链接到 Key Vault 的变量组会在运行时获取密钥——模板表达式(
${{ }})无法访问 Key Vault 密钥,因为它们会在编译时解析;请改用运行时表达式($())。 - 经典发布管道不会存储在源代码管理中——这是迁移的主要动机;YAML 管道支持 PR 审查和特定分支的定义。
- 管道装饰器无法被管道作者绕过——这是为了强制执行策略;请在单独的组织或项目中测试装饰器更改,以免破坏所有管道。
- 通用包的单个文件大小上限为 4 GiB——对于更大的工件,请拆分文件,或改用带有 SAS 令牌的 Azure Blob Storage。