dotnet-ado-patterns について
.NET チーム向けに、Azure DevOps YAML パイプラインの構成パターンを扱うスキルです。再利用可能なテンプレート、設定の集約、条件挿入、複数ステージやトリガーの設計を解説します。
主な機能
- 01ステージ・ジョブ・ステップのテンプレート化
- 02extends による構造の強制
- 03変数グループとテンプレートの集約
- 04式によるパイプライン内容の生成
- 05複数ステージとトリガーの設定
活用シーン
- 01.NET パイプライン間でビルド手順を再利用
- 02必須のセキュリティ・コンプライアンス段階を追加
- 03環境に応じたデプロイフローを生成
- 04CI・PR・スケジュール実行を設定
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ wshaddix/ dotnet-skills --skill dotnet-ado-patternsインストール後、Claude Code で/dotnet-ado-patternsを入力して読み込まれたか確認します。
詳細
dotnet-ado-patterns
.NET プロジェクト向けのコンポーザブルな Azure DevOps YAML パイプラインパターン:extends、stages、jobs、steps キーワードを使ったテンプレート参照による階層的なパイプライン構成、一元管理された設定のための変数グループと変数テンプレート、組織全体へのポリシー注入に使用するパイプラインデコレーター、${{ if }} と ${{ each }} 式による条件付き挿入、マルチステージパイプライン(ビルド、テスト、デプロイ)、および CI、PR、スケジュール実行のためのパイプライントリガー。
バージョンの前提条件: Azure Pipelines YAML スキーマ。.NET 8/9/10 のビルドには DotNetCoreCLI@2 タスクを使用します。テンプレート式の構文は v2 です。
対象範囲: このスキルは Azure DevOps YAML のコンポーザブルなパイプライン設計パターンを担当します。スターター CI テンプレート(基本的なビルド/テスト/パック)は [skill:dotnet-add-ci] が担当します。このスキルは、これらのテンプレートを高度な構成で拡張します。CLI バイナリ向けの CLI 固有のリリースパイプライン(build-package-release)は [skill:dotnet-cli-release-pipeline] が担当します。このスキルでは、CLI パイプラインで使用する汎用的なパイプラインパターンを扱います。環境と承認、サービス接続、クラシックリリースなどの ADO 固有機能については [skill:dotnet-ado-unique] を参照してください。
対象外: スターター CI テンプレートについては [skill:dotnet-add-ci] を参照してください。CLI リリースパイプライン(CLI ツール向けのタグトリガー型 build-package-release)については [skill:dotnet-cli-release-pipeline] を参照してください。ADO 固有機能(環境、サービス接続、クラシックリリース)については [skill:dotnet-ado-unique] を参照してください。ビルド/テストの詳細については [skill:dotnet-ado-build-test] を参照してください。公開パイプラインについては [skill:dotnet-ado-publish] を参照してください。GitHub Actions のワークフローパターンについては [skill:dotnet-gha-patterns] を参照してください。
相互参照:[skill:dotnet-add-ci] では、これらのパターンで拡張するスターターテンプレートを扱います。[skill:dotnet-cli-release-pipeline] では、CLI 固有のリリース自動化を扱います。
テンプレート参照
ステージテンプレート
ステージテンプレートは、呼び出し元がマルチステージパイプラインに挿入する、再利用可能なパイプラインステージを定義します。
# templates/stages/build-test.yml
parameters:
- name: dotnetVersion
type: string
default: '8.0.x'
- name: buildConfiguration
type: string
default: 'Release'
- name: projects
type: string
default: '**/*.sln'
stages:
- stage: Build
displayName: 'Build and Test'
jobs:
- job: BuildJob
pool:
vmImage: 'ubuntu-latest'
steps:
- task: UseDotNet@2
displayName: 'Install .NET SDK'
inputs:
packageType: 'sdk'
version: ${{ parameters.dotnetVersion }}
- task: DotNetCoreCLI@2
displayName: 'Restore'
inputs:
command: 'restore'
projects: ${{ parameters.projects }}
- task: DotNetCoreCLI@2
displayName: 'Build'
inputs:
command: 'build'
projects: ${{ parameters.projects }}
arguments: '-c ${{ parameters.buildConfiguration }} --no-restore'
ステージテンプレートの呼び出し
# azure-pipelines.yml
trigger:
branches:
include:
- main
stages:
- template: templates/stages/build-test.yml
parameters:
dotnetVersion: '9.0.x'
buildConfiguration: 'Release'
projects: 'MyApp.sln'
- template: templates/stages/deploy.yml
parameters:
environment: 'staging'
ジョブテンプレート
ジョブテンプレートは、プールとステップを含むジョブ全体をカプセル化します。
# templates/jobs/dotnet-build.yml
parameters:
- name: dotnetVersion
type: string
default: '8.0.x'
- name: projects
type: string
jobs:
- job: Build
pool:
vmImage: 'ubuntu-latest'
steps:
- task: UseDotNet@2
inputs:
packageType: 'sdk'
version: ${{ parameters.dotnetVersion }}
- task: DotNetCoreCLI@2
displayName: 'Build'
inputs:
command: 'build'
projects: ${{ parameters.projects }}
arguments: '-c Release'
ステップテンプレート
ステップテンプレートは、既存のジョブに挿入する再利用可能なステップシーケンスを定義します。
# templates/steps/dotnet-setup.yml
parameters:
- name: dotnetVersion
type: string
default: '8.0.x'
- name: nugetFeed
type: string
default: ''
steps:
- task: UseDotNet@2
displayName: 'Install .NET SDK ${{ parameters.dotnetVersion }}'
inputs:
packageType: 'sdk'
version: ${{ parameters.dotnetVersion }}
- ${{ if ne(parameters.nugetFeed, '') }}:
- task: NuGetAuthenticate@1
displayName: 'Authenticate NuGet feed'
- task: DotNetCoreCLI@2
displayName: 'Restore packages'
inputs:
command: 'restore'
projects: '**/*.sln'
${{ if ne(parameters.nugetFeed, '') }}:
feedsToUse: 'select'
vstsFeed: ${{ parameters.nugetFeed }}
パイプラインでのステップテンプレートの使用
jobs:
- job: Build
pool:
vmImage: 'ubuntu-latest'
steps:
- checkout: self
- template: templates/steps/dotnet-setup.yml
parameters:
dotnetVersion: '9.0.x'
nugetFeed: 'MyOrg/MyFeed'
- task: DotNetCoreCLI@2
displayName: 'Build'
inputs:
command: 'build'
arguments: '-c Release --no-restore'
extends テンプレート(強制されるパイプライン構造)
extends キーワードは、組織テンプレートで定義された必須のパイプライン構造を強制します。呼び出し元がこの構造を回避することはできません。
# templates/pipeline-policy.yml
parameters:
- name: stages
type: stageList
default: []
stages:
- stage: SecurityScan
displayName: 'Security Scan (Required)'
jobs:
- job: Scan
pool:
vmImage: 'ubuntu-latest'
steps:
- script: echo "Running mandatory security scan"
- ${{ each stage in parameters.stages }}:
- ${{ stage }}
- stage: Compliance
displayName: 'Compliance Check (Required)'
dependsOn:
- ${{ each stage in parameters.stages }}:
- ${{ stage.stage }}
jobs:
- job: Check
pool:
vmImage: 'ubuntu-latest'
steps:
- script: echo "Running compliance checks"
# azure-pipelines.yml (caller)
extends:
template: templates/pipeline-policy.yml
parameters:
stages:
- stage: Build
jobs:
- job: BuildApp
pool:
vmImage: 'ubuntu-latest'
steps:
- script: dotnet build -c Release
extends テンプレートは、呼び出し元が定義するステージを、削除できない必須のセキュリティステージとコンプライアンスステージでラップします。
変数グループと変数テンプレート
変数グループ
変数グループは、複数のパイプラインで共有する設定を一元管理します。Azure Pipelines ライブラリからリンクします。
variables:
- group: 'dotnet-build-settings'
- group: 'nuget-feed-credentials'
- name: buildConfiguration
value: 'Release'
変数テンプレート
変数テンプレートは、YAML ファイルで再利用可能な変数セットを定義します。
# templates/variables/dotnet-defaults.yml
variables:
dotnetVersion: '8.0.x'
buildConfiguration: 'Release'
testResultsDirectory: '$(Build.ArtifactStagingDirectory)/test-results'
coverageDirectory: '$(Build.ArtifactStagingDirectory)/coverage'
# azure-pipelines.yml
variables:
- template: templates/variables/dotnet-defaults.yml
- name: projectPath
value: 'MyApp.sln'
Key Vault 統合を使用する変数グループ
シークレットを管理するには、変数グループを Azure Key Vault にリンクします。シークレットはパイプラインの実行時に取得されます。
# Reference in pipeline
variables:
- group: 'kv-production-secrets' # linked to Azure Key Vault
- name: nonSecretVar
value: 'some-value'
steps:
- script: |
echo "Using secret from Key Vault"
# $(sql-connection-string) resolves at runtime from Key Vault
env:
CONNECTION_STRING: $(sql-connection-string)
Key Vault にリンクされた変数グループには、Key Vault へのアクセス権を持つサービス接続が必要です。Key Vault 内のシークレット名は変数名に対応します(ハイフンは有効な変数文字に変換されます)。
パイプラインデコレーター
パイプラインデコレーターは、個々のパイプラインファイルを変更せずにポリシーを適用できるよう、組織またはプロジェクト内のすべてのパイプラインにステップを注入します。デコレーターは ADO 専用の機能で、GitHub Actions に相当する機能はありません。拡張機能マニフェスト、デプロイのガイダンス、ユースケースの例など、実装の詳細については [skill:dotnet-ado-unique] を参照してください。
条件付き挿入
${{ if }} 式
parameters:
- name: runIntegrationTests
type: boolean
default: false
- name: targetEnvironment
type: string
default: 'development'
values:
- development
- staging
- production
stages:
- stage: Build
jobs:
- job: BuildJob
steps:
- script: dotnet build -c Release
- ${{ if eq(parameters.runIntegrationTests, true) }}:
- stage: IntegrationTests
dependsOn: Build
jobs:
- job: IntegrationTestJob
steps:
- script: dotnet test --filter Category=Integration
- ${{ if eq(parameters.targetEnvironment, 'production') }}:
- stage: ApprovalGate
dependsOn: Build
jobs:
- job: WaitForApproval
pool: server
steps:
- task: ManualValidation@0
inputs:
notifyUsers: '[email protected]'
instructions: 'Approve production deployment'
${{ each }} による反復処理
parameters:
- name: environments
type: object
default:
- name: development
pool: 'ubuntu-latest'
approvals: false
- name: staging
pool: 'ubuntu-latest'
approvals: true
- name: production
pool: 'ubuntu-latest'
approvals: true
stages:
- stage: Build
jobs:
- job: BuildJob
steps:
- script: dotnet build -c Release
- ${{ each env in parameters.environments }}:
- stage: Deploy_${{ env.name }}
displayName: 'Deploy to ${{ env.name }}'
dependsOn: Build
jobs:
- ${{ if eq(env.approvals, true) }}:
- job: Approve
pool: server
steps:
- task: ManualValidation@0
inputs:
instructions: 'Approve deployment to ${{ env.name }}'
- deployment: DeployApp
pool:
vmImage: ${{ env.pool }}
environment: ${{ env.name }}
strategy:
runOnce:
deploy:
steps:
- script: echo "Deploying to ${{ env.name }}"
テンプレート内での条件付きステップ挿入
# templates/steps/dotnet-test.yml
parameters:
- name: collectCoverage
type: boolean
default: false
steps:
- task: DotNetCoreCLI@2
displayName: 'Run tests'
inputs:
command: 'test'
projects: '**/*Tests.csproj'
${{ if eq(parameters.collectCoverage, true) }}:
arguments: '-c Release --collect:"XPlat Code Coverage"'
${{ else }}:
arguments: '-c Release'
- ${{ if eq(parameters.collectCoverage, true) }}:
- task: PublishCodeCoverageResults@2
displayName: 'Publish coverage'
inputs:
summaryFileLocation: '$(Agent.TempDirectory)/**/coverage.cobertura.xml'
マルチステージパイプライン
ビルド、テスト、デプロイのパターン
trigger:
branches:
include:
- main
- release/*
stages:
- stage: Build
displayName: 'Build'
jobs:
- job: BuildJob
pool:
vmImage: 'ubuntu-latest'
steps:
- task: UseDotNet@2
inputs:
packageType: 'sdk'
version: '8.0.x'
- task: DotNetCoreCLI@2
displayName: 'Build'
inputs:
command: 'build'
projects: 'MyApp.sln'
arguments: '-c Release'
- task: DotNetCoreCLI@2
displayName: 'Publish'
inputs:
command: 'publish'
projects: 'src/MyApp/MyApp.csproj'
arguments: '-c Release -o $(Build.ArtifactStagingDirectory)/app'
- task: PublishPipelineArtifact@1
displayName: 'Upload artifact'
inputs:
targetPath: '$(Build.ArtifactStagingDirectory)/app'
artifactName: 'app'
- stage: Test
displayName: 'Test'
dependsOn: Build
jobs:
- job: UnitTests
pool:
vmImage: 'ubuntu-latest'
steps:
- task: UseDotNet@2
inputs:
packageType: 'sdk'
version: '8.0.x'
- task: DotNetCoreCLI@2
displayName: 'Run tests'
inputs:
command: 'test'
projects: '**/*Tests.csproj'
arguments: '-c Release --logger "trx;LogFileName=results.trx"'
- task: PublishTestResults@2
displayName: 'Publish test results'
condition: always()
inputs:
testResultsFormat: 'VSTest'
testResultsFiles: '**/results.trx'
- stage: DeployStaging
displayName: 'Deploy to Staging'
dependsOn: Test
jobs:
- deployment: DeployStaging
pool:
vmImage: 'ubuntu-latest'
environment: 'staging'
strategy:
runOnce:
deploy:
steps:
- download: current
artifact: app
- script: echo "Deploying to staging"
- stage: DeployProduction
displayName: 'Deploy to Production'
dependsOn: DeployStaging
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
jobs:
- deployment: DeployProduction
pool:
vmImage: 'ubuntu-latest'
environment: 'production'
strategy:
runOnce:
deploy:
steps:
- download: current
artifact: app
- script: echo "Deploying to production"
ステージの依存関係と条件
stages:
- stage: Build
jobs:
- job: BuildJob
steps:
- script: dotnet build -c Release
- stage: UnitTests
dependsOn: Build
jobs:
- job: UnitTestJob
steps:
- script: dotnet test --filter Category!=Integration
- stage: IntegrationTests
dependsOn: Build
jobs:
- job: IntegrationTestJob
steps:
- script: dotnet test --filter Category=Integration
# Deploy only if BOTH test stages succeed
- stage: Deploy
dependsOn:
- UnitTests
- IntegrationTests
condition: and(succeeded('UnitTests'), succeeded('IntegrationTests'))
jobs:
- deployment: DeployApp
environment: 'production'
strategy:
runOnce:
deploy:
steps:
- script: echo "Deploying"
パイプライントリガー
CI トリガー
trigger:
branches:
include:
- main
- release/*
exclude:
- feature/experimental/*
paths:
include:
- src/**
- tests/**
- '*.sln'
- Directory.Build.props
- Directory.Packages.props
exclude:
- docs/**
- '*.md'
tags:
include:
- 'v*'
PR トリガー
pr:
branches:
include:
- main
- release/*
paths:
include:
- src/**
- tests/**
exclude:
- docs/**
drafts: false # do not trigger on draft PRs
スケジュールトリガー
schedules:
- cron: '0 6 * * 1-5'
displayName: 'Weekday nightly build'
branches:
include:
- main
always: false # only run if there are changes since last run
- cron: '0 0 * * 0'
displayName: 'Weekly full validation'
branches:
include:
- main
always: true # run even without changes
パイプラインリソーストリガー
別のパイプラインの完了時にパイプラインをトリガーします。
resources:
pipelines:
- pipeline: buildPipeline
source: 'MyApp-Build'
trigger:
branches:
include:
- main
stages:
- stage: DeployAfterBuild
jobs:
- deployment: Deploy
environment: 'staging'
strategy:
runOnce:
deploy:
steps:
- download: buildPipeline
artifact: app
- script: echo "Deploying build from upstream pipeline"
エージェント利用時の注意点
- テンプレートパラメーターの型はコンパイル時に適用されます --
type: booleanが期待される箇所に文字列を渡すと、パイプラインの実行前に検証エラーが発生します。型は必ず正確に一致させてください。 extendsテンプレートは上書きできません -- 呼び出し元から、必須ステージの前後にステップを挿入することはできません。これはポリシーを適用するための仕様です。- 変数グループのシークレットはテンプレート式では利用できません -- シークレットがまだ利用可能でないコンパイル時に
${{ variables.mySecret }}を評価すると、解決されません。代わりに$(mySecret)の実行時構文を使用してください。 ${{ each }}はコンパイル時に反復処理されます -- ループはパイプラインの実行前に YAML を生成します。実行時変数を反復処理の対象として使用することはできません。- CI トリガーと PR トリガーは
trigger: noneおよびpr: noneとは併用できません --triggerセクションとprセクションの両方を省略すると、すべてのブランチで既定の CI トリガーが有効になります。無効にするには、明示的にtrigger: noneを設定してください。 - トリガーのパスフィルターは、リポジトリのルートからの相対パスを使用します -- パスの先頭に
/や./を付けないでください。./src/**ではなくsrc/**を使用してください。 - スケジュールトリガーは、最初に必ず既定のブランチで実行されます --
branches.includeフィルターはスケジュールが発火した後に適用されます。スケジュール自体は既定のブランチの YAML からのみ評価されます。 - パイプラインリソーストリガーでは、YAML ファイルのパスではなくソースパイプラインの名前が必要です --
azure-pipelines.ymlファイルのパスではなく、ADO に表示されるパイプライン名を使用してください。