| name | azure-devops-pipelines |
| description | Designs and implements production-grade Azure DevOps CI/CD pipelines using YAML-as-code for .NET and Laravel/PHP enterprise projects. Use when creating or refactoring Azure Pipelines, configuring multi-stage pipelines, setting up build/test/deploy stages, implementing caching strategies for NuGet or Composer, integrating Azure Key Vault for secret management, configuring branch policies, adding approval gates before production, parallelizing test execution with strategy matrix, or asked about pipeline security, service connections, workload identity, or DORA metrics in Azure DevOps.
|
| license | MIT |
| metadata | {"author":"iamBrzDev","version":"1.0"} |
When to activate
- Creating a new Azure DevOps pipeline from scratch
- Migrating a Classic (UI) pipeline to YAML
- "How do I speed up my pipeline?" in an Azure DevOps context
- Adding caching for NuGet packages, Composer vendor/, or node_modules
- Configuring secrets — any mention of Key Vault, service connections, or tokens in YAML
- Setting up multi-stage pipelines (build → test → deploy)
- Parallelizing tests with strategy matrix or test sharding
- Adding approval gates or branch policies before production deploy
- Any mention of DORA metrics, lead time, change failure rate in CI/CD context
Rules — Non-negotiable
-
Classic pipelines are legacy. Always use YAML. Every pipeline must be a
versioned .yml file in the repository. No exceptions.
-
Secrets never go in YAML files. Use Azure Key Vault + Managed Identity
or Variable Groups linked to Key Vault. Never hardcode tokens, passwords,
or connection strings — not even as pipeline variables in plain text.
-
Stages must have explicit dependencies. Use dependsOn to enforce
execution order. Deploy never runs without a passing Test stage.
-
Cache dependencies, always. A pipeline without caching is wasting
40–60% of its build time on redundant downloads.
-
Least privilege on service connections. A build pipeline must not have
write access to production resources. Scope permissions per environment.
Pipeline Structure
Always enforce this multi-stage structure. Adapt stages to the project but
never skip the dependency chain:
Trigger → Build → Test (parallel) → Security Scan → Deploy (gated)
Canonical YAML skeleton
trigger:
branches:
include:
- main
- develop
pr:
branches:
include:
- main
variables:
- group: kv-enterprise-secrets
- name: buildConfiguration
value: Release
stages:
- stage: Build
displayName: 'Build'
jobs:
- job: BuildJob
pool:
vmImage: ubuntu-latest
steps:
- task: Cache@2
...
- script: echo "Build steps here"
- stage: Test
displayName: 'Test'
dependsOn: Build
jobs:
- job: UnitTests
...
- job: IntegrationTests
...
- stage: SecurityScan
displayName: 'Security Scan'
dependsOn: Build
- stage: Deploy
displayName: 'Deploy to Production'
dependsOn:
- Test
- SecurityScan
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
jobs:
- deployment: DeployProd
environment: production
...
Caching — .NET (NuGet)
- task: Cache@2
inputs:
key: 'nuget | "$(Agent.OS)" | **/packages.lock.json,!**/bin/**,!**/obj/**'
restoreKeys: |
nuget | "$(Agent.OS)"
path: $(NUGET_PACKAGES)
displayName: 'Cache NuGet packages'
- task: DotNetCoreCLI@2
inputs:
command: restore
projects: '**/*.csproj'
feedsToUse: select
Caching — Laravel (Composer + NPM)
- task: Cache@2
inputs:
key: 'composer | "$(Agent.OS)" | composer.lock'
restoreKeys: composer | "$(Agent.OS)"
path: vendor
displayName: 'Cache Composer dependencies'
- task: Cache@2
inputs:
key: 'npm | "$(Agent.OS)" | package-lock.json'
restoreKeys: npm | "$(Agent.OS)"
path: node_modules
displayName: 'Cache NPM dependencies'
- script: composer install --no-interaction --prefer-dist --optimize-autoloader
displayName: 'Install Composer dependencies'
Parallelization — Test Matrix
Reduce a 50-minute test suite to ~10 minutes by splitting across agents:
- stage: Test
jobs:
- job: TestMatrix
strategy:
matrix:
UnitTests:
testCategory: 'Unit'
IntegrationTests:
testCategory: 'Integration'
ContractTests:
testCategory: 'Contract'
maxParallel: 3
steps:
- script: dotnet test --filter "Category=$(testCategory)"
displayName: 'Run $(testCategory) tests'
Secret Management — Azure Key Vault
variables:
- group: kv-production-secrets
steps:
- script: echo "Connection string is $(SqlConnectionString)"
variables:
SqlConnectionString: 'Server=prod-db;Password=SuperSecret123'
Setting up Workload Identity Federation (preferred over service principals)
- task: AzureCLI@2
inputs:
azureSubscription: 'workload-identity-connection'
scriptType: bash
scriptLocation: inlineScript
inlineScript: |
az keyvault secret show --name SqlPassword --vault-name kv-enterprise
Branch Policies and Approval Gates
Configure in Azure DevOps UI under Environments → Approvals and checks,
or enforce via pipeline:
- stage: Deploy
jobs:
- deployment: DeployProduction
environment: production
strategy:
runOnce:
deploy:
steps:
- script: echo "Deploying after approval"
Branch policies to always enable on main:
- Minimum 1 reviewer approval
- Build validation (pipeline must pass)
- Comment resolution required
- No direct pushes
Templates — Reusable Steps
Centralize common steps in a templates/ directory to avoid duplication:
parameters:
- name: configuration
type: string
default: Release
steps:
- task: DotNetCoreCLI@2
inputs:
command: build
arguments: '--configuration ${{ parameters.configuration }}'
steps:
- template: templates/dotnet-build.yml
parameters:
configuration: $(buildConfiguration)
Common mistakes
-
❌ Storing secrets as plain pipeline variables visible in the UI
-
✅ Link Variable Groups to Azure Key Vault — secrets are never visible in plain text
-
❌ All stages in a single job with sequential steps — slow and fragile
-
✅ Separate jobs per concern, use dependsOn and parallelization
-
❌ Deploy stage with no environment — bypasses approval gate capability
-
✅ Always use deployment job type with environment: to enable gates
-
❌ Giving service connection Contributor access to the entire subscription
-
✅ Scope service connections to specific resource groups per environment
Definition of Done
A pipeline built with this skill is complete only when:
Reference files
Load on demand:
references/variable-groups-keyvault.md — step-by-step Key Vault integration
references/test-sharding-dotnet.md — advanced test parallelization for large .NET suites
references/laravel-full-pipeline.md — complete Laravel pipeline with deploy to App Service