Generates production-ready Azure DevOps Pipelines (azure-pipelines.yml) following current best practices, security standards, and naming conventions. Use when creating or updating ADO YAML pipelines, configuring build triggers, defining multi-stage deployments, setting up template references, creating variable groups, writing release pipelines, or structuring CI/CD workflows for Azure DevOps Services or Azure DevOps Server. Handles build pipelines, YAML pipelines, Docker container builds, Kubernetes/AKS deployments, language-specific pipelines (.NET, Node.js, Python, Go, Java), and reusable step/job/stage templates. All generated configurations are validated using the devops-skills:azure-pipelines-validator skill before delivery.
69
87%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
Generate production-ready Azure DevOps Pipeline configurations following current best practices, security standards, and naming conventions. After generating any complete pipeline file, always validate it using the devops-skills:azure-pipelines-validator skill, fix any reported issues, and re-validate before presenting to the user. Skip validation only for partial snippets, documentation examples, or when the user explicitly requests it.
azure-pipelines.yml for build, test or deploy.Read references/yaml-schema.md, references/best-practices.md, references/tasks-reference.md, and assets/examples/basic-ci.yml.
Example:
trigger:
branches:
include:
- main
- develop
pool:
vmImage: 'ubuntu-22.04'
variables:
buildConfiguration: 'Release'
steps:
- task: NodeTool@0
displayName: 'Install Node.js'
inputs:
versionSpec: '20.x'
- task: Cache@2
displayName: 'Cache npm packages'
inputs:
key: 'npm | "$(Agent.OS)" | package-lock.json'
path: $(Pipeline.Workspace)/.npm
- script: npm ci --cache $(Pipeline.Workspace)/.npm
displayName: 'Install dependencies'
- script: npm run build
displayName: 'Build application'
- script: npm test
displayName: 'Run tests'
- task: PublishTestResults@2
condition: succeededOrFailed()
inputs:
testResultsFormat: 'JUnit'
testResultsFiles: '**/test-results.xml'Read references/yaml-schema.md and assets/examples/multi-stage-cicd.yml. Use deployment jobs for environment tracking; publish artifacts between stages.
Example:
stages:
- stage: Build
displayName: 'Build Stage'
jobs:
- job: BuildJob
displayName: 'Build Application'
pool:
vmImage: 'ubuntu-22.04'
steps:
- script: npm run build
displayName: 'Build'
- publish: $(Build.SourcesDirectory)/dist
artifact: drop
- stage: Test
displayName: 'Test Stage'
dependsOn: Build
jobs:
- job: TestJob
displayName: 'Run Tests'
steps:
- script: npm test
displayName: 'Test'
- stage: DeployProd
displayName: 'Deploy to Production'
dependsOn: Test
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
jobs:
- deployment: DeployProd
environment: production
strategy:
runOnce:
deploy:
steps:
- script: echo "Deploying"Read references/tasks-reference.md for Docker@2 and assets/examples/kubernetes-deploy.yml. Use service connection for authentication; tag with $(Build.BuildId) as primary.
Example:
variables:
dockerRegistryServiceConnection: 'myACR'
imageRepository: 'myapp'
containerRegistry: 'myregistry.azurecr.io'
tag: '$(Build.BuildId)'
steps:
- task: Docker@2
displayName: 'Build and Push'
inputs:
command: buildAndPush
repository: $(imageRepository)
dockerfile: '$(Build.SourcesDirectory)/Dockerfile'
containerRegistry: $(dockerRegistryServiceConnection)
tags: |
$(tag)
latestTagging rule: Push with
$(tag)ANDlatest; deploy/pull using only the specific$(tag)— never:latestin production deployments.
Read references/tasks-reference.md and assets/examples/kubernetes-deploy.yml. Use KubernetesManifest@0 or Kubernetes@1; include namespace management and health checks.
Example:
- task: KubernetesManifest@0
displayName: 'Deploy to Kubernetes'
inputs:
action: 'deploy'
kubernetesServiceConnection: 'myK8sCluster'
namespace: 'production'
manifests: |
k8s/deployment.yml
k8s/service.yml
containers: '$(containerRegistry)/$(imageRepository):$(tag)'Supported languages: .NET/C#, Node.js, Python, Java, Go, Docker multi-stage
Read references/tasks-reference.md and the matching example file:
| Language | Example File |
|---|---|
| Go | assets/examples/go-cicd.yml |
| .NET/C# | assets/examples/dotnet-cicd.yml |
| Python | assets/examples/python-cicd.yml |
| Node.js | assets/examples/basic-ci.yml or multi-stage-cicd.yml |
Include: runtime setup, package manager caching, build, test with reporting, artifact publish.
Go-specific notes:
GoTool@0 (only major version available — @0 is correct)$(GOPATH)/pkg/mod using go.sum as keygo vet ./... before tests; use -race -coverprofile flags for test coverageCGO_ENABLED=0 for container imagesMatrix testing pattern:
strategy:
matrix:
node18:
nodeVersion: '18.x'
node20:
nodeVersion: '20.x'
node22:
nodeVersion: '22.x'
maxParallel: 3
steps:
- task: NodeTool@0
inputs:
versionSpec: $(nodeVersion)
- script: npm testRead references/templates-guide.md and assets/examples/templates/. Use ${{ parameters.name }} syntax; generate both template and consuming pipeline.
Example:
# templates/build.yml
parameters:
- name: nodeVersion
type: string
default: '20.x'
steps:
- task: NodeTool@0
inputs:
versionSpec: ${{ parameters.nodeVersion }}
- script: npm ci
- script: npm run build
# Main pipeline
steps:
- template: templates/build.yml
parameters:
nodeVersion: '20.x'When local docs are sufficient (most cases):
references/tasks-reference.md covers .NET, Node.js, Python, Go, Docker, Kubernetes, Azure tasksreferences/yaml-schema.md covers complete YAML syntaxWhen to use external sources (tasks not in local docs, version-specific questions, troubleshooting):
mcp__context7__resolve-library-id → query "azure-pipelines" → mcp__context7__get-library-docs"[TaskName]@[version] Azure Pipelines task documentation"Analyze retrieved docs for: task name/version, required vs optional inputs, service connection requirements, and outputs.
# Example: task found via documentation lookup
- task: AzureFunctionApp@1
displayName: 'Deploy Azure Function'
inputs:
azureSubscription: 'AzureServiceConnection' # Required: ARM service connection
appType: 'functionAppLinux'
appName: 'myfunctionapp'
package: '$(Build.ArtifactStagingDirectory)/**/*.zip'
runtimeStack: 'NODE|20'Reference references/best-practices.md for comprehensive guidelines.
ubuntu-22.04 not ubuntu-latestDocker@2 not Docker (pin to major version; @0 is correct for GoTool@0, NodeTool@0, KubernetesManifest@0)'20.x' for Node.js, explicit Go versionsCache@2 for all package managers; use dependsOn for parallelism; set artifact expiration; shallow clone when full history is unnecessaryBuildAndTest, DeployProduction)displayName: Sentence case ('Build application', 'Run tests')timeoutInMinutes; use condition: succeededOrFailed() for test publishing; continueOnError for non-critical stepsPublishTestResults@2) and code coverage (PublishCodeCoverageResults@1)For a complete end-to-end workflow example (Understanding → Reading → Lookup → Generate → Validate → Present), see references/typical-workflow.md.
latest for task version pinsWHY: ADO task versions introduce breaking changes across major versions. Using @latest or an unpinned reference creates non-deterministic builds where a task update can silently break your pipeline overnight.
BAD: - task: UseNode@latest
GOOD: - task: UseNode@0 with a pinned versionSpec input (e.g., versionSpec: '20.x').
WHY: YAML variables are committed to source control and visible in pipeline run logs, exposing credentials to anyone with repository read access or pipeline view permissions.
BAD: variables: API_KEY: 'abc123'
GOOD: Use Azure Key Vault task or pipeline variable groups with the "secret" flag enabled in the ADO UI.
displayName: on tasks and stepsWHY: Pipelines without display names produce cryptic logs like Task 1 of 12 that are impossible to interpret when diagnosing a failure, especially in multi-stage pipelines.
BAD: - script: npm ci with no displayName.
GOOD: - script: npm ci followed by displayName: 'Install dependencies'.
trigger: none on templates used as main pipelinesWHY: trigger: none disables all automatic triggers, meaning the pipeline never runs on code push. This is appropriate only for templates called by other pipelines, not for CI entry-point pipelines.
BAD: trigger: none on a pipeline intended to run on every commit.
GOOD: Configure explicit branch includes, for example trigger: branches: include: [main, develop].
WHY: Single-file pipelines exceeding a few hundred lines become unmaintainable, impossible to test in isolation, and prone to merge conflicts when multiple teams update them simultaneously.
BAD: A 400-line azure-pipelines.yml with all stages, jobs, and scripts inlined.
GOOD: Extract stage and job logic into separate templates/*.yml files and reference them with - template: templates/build.yml.
ubuntu-latest or other floating vmImage valuesWHY: a floating image changes underneath the pipeline, so builds stop being reproducible and can break without any code change.
BAD: vmImage: 'ubuntu-latest'
GOOD: vmImage: 'ubuntu-22.04'
latest image tag in productionWHY: latest is mutable, so the running version cannot be traced to a build and rollbacks are ambiguous.
BAD: a Kubernetes manifest deploying $(imageRepository):latest.
GOOD: push with $(tag) and latest, but deploy using only $(tag) (the $(Build.BuildId)).
WHY: only deployment jobs give environment tracking, approvals and deployment history.
BAD: a job: that runs kubectl apply for the production stage.
GOOD: a deployment: job with environment: production and a runOnce strategy.
WHY: failed runs are exactly where the test report is needed, and a skipped publish hides which tests failed.
BAD: PublishTestResults@2 with no condition, or no publish step at all.
GOOD: condition: succeededOrFailed() on PublishTestResults@2, plus code coverage publishing.
WHY: unvalidated YAML ships syntax, hierarchy and security errors straight to the user.
BAD: presenting a generated azure-pipelines.yml straight away.
GOOD: run the azure-pipelines-validator skill, fix reported issues, and re-validate before presenting.
| Error type | Resolution |
|---|---|
| Syntax errors | Fix YAML indentation or structure |
| Task version errors | Ensure format is TaskName@version |
| Pool/vmImage errors | Use specific versions, not latest |
| Stage/Job errors | Verify stages → jobs → steps hierarchy |
| Security warnings | Remove hardcoded secrets; avoid :latest in deployments |
| Topic | Reference | When to Use |
|---|---|---|
| YAML schema | references/yaml-schema.md | Pipeline structure, triggers, pools, variables, conditions, expressions |
| Task catalogue | references/tasks-reference.md | Task inputs, outputs and service connection requirements |
| Best practices | references/best-practices.md | Security, performance, naming and anti-patterns |
| Templates | references/templates-guide.md | Template types, parameter definitions, expressions, iteration |
| End-to-end workflow | references/typical-workflow.md | A complete worked example including validation steps |
| Basic CI example | assets/examples/basic-ci.yml | Simple CI, single-stage builds |
| Multi-stage example | assets/examples/multi-stage-cicd.yml | Multi-environment deployments |
| Kubernetes example | assets/examples/kubernetes-deploy.yml | Docker plus K8s/AKS deployments |
| Go example | assets/examples/go-cicd.yml | Go applications |
| .NET example | assets/examples/dotnet-cicd.yml | .NET/C# applications |
| Python example | assets/examples/python-cicd.yml | Python applications |
| Template usage example | assets/examples/template-usage.yml | Template-consuming pipelines |
| Build template | assets/examples/templates/build-template.yml | Reusable build templates |
| Deploy template | assets/examples/templates/deploy-template.yml | Reusable deployment templates |
pantheon-org/tekhne
48ef197
Also appears in
last in sync Aug 28, 2026
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.