Skip to content
devops-specialist logo

devops-specialist

Deep domain knowledge for DevOps — Azure DevOps pipeline templates, Docker multi-stage builds, Helm/K8s deployment patterns, environment promotion flow, Confix configuration management, and pipeline troubleshooting. Triggers on: azure-pipelines YAML, Dockerfile, Helm values.yaml, k8s-deployment, ...

SKILL.md

Full skill instructions

DevOps Specialist

Domain knowledge for CI/​CD pipelines, Docker builds, Helm/​K8s deployments, and environment promotion.

Scope: Project-specific DevOps patterns only. For general DevOps workflow and investigation → DevOps Expert agent.

Azure DevOps Organization

<!-- TODO: Replace with your actual Azure DevOps project names and purposes -->
ProjectPurpose
MainMain development — Backend services, Pipeline-Templates
ReleaseManagementChange/​Test/​Release Management — wikis, release process docs, Developer-Handbook
IaCInfrastructure as Code — Terraform modules, Azure resource provisioning

Key repos in the Main project:

  • My-Backend — main monorepo for all domain services
  • Pipeline-Templates — shared pipeline templates (Deployment/​k8s-deployment.yaml, variable templates)

Branching Strategy

GitHub Flow:

  • master is always deployable
  • Feature branches from master, PRs trigger validation (test-pr + sonar-pr)
  • Tags on master trigger release pipelines
  • Preview tags on feature branches deploy to A environment

Pipeline Architecture

Pipeline Types

PipelineTriggerPurpose
test-prPR to masterUnit/​integration tests, build validation
sonar-prPR to masterSonarQube code quality analysis
releaseTag on masterBuild Docker images, push to ACR, deploy

Repository Pipeline Structure

.devops/
├── azure-pipelines.release-<Service>.yml
├── azure-pipelines.sonar-pr.yml
├── azure-pipelines.test-pr.yml
└── templates/
    ├── template.detect-changes.yml
    ├── template.release.yml
    ├── template.sonar-pr.yml
    └── template.test-pr.yml

Shared Templates (Pipeline-Templates)

K8s Deployment Template (Deployment/​k8s-deployment.yaml) key parameters:

ParameterPurpose
DockerImageNameDocker image name
HelmReleaseNameHelm release name in cluster
HelmChartNameDefaults to deployment-ng (<!-- TODO: replace with your chart name -->)
HelmChartVersionChart version (e.g., 12.12.0)
EnvironmentTarget: A, A2, UAT, UAT2, PAV, PAV2, DEV001
NamespaceKubernetes namespace
KubernetesFilePathPath to K8s values (default: app/​k8s)

Agent pool: scm-vmss-agentpool-001 (fallback: ubuntu-latest)

Environment Variable Templates

Per-environment variables from k8s-vars-{env}.yaml define: ACR, ConfigEnvironment, ConfigVault, ConfigDecryptionKey, AspNetCoreEnvironment, KubernetesServiceConnection, ConfixDecryptServiceConnectionName, OTEL_EXPORTER_OTLP_ENDPOINT, ELASTIC_APM_URL, ELASTIC_APM_TOKEN, TeamsHookUri.

Environments & Promotion

EnvironmentPurposeDeploymentApproval
A / A2DevelopmentAuto on preview tags / ManualNone
UAT / UAT2Business testingTag-triggered / ManualMay require approval
PAV / PAV2ProductionTag-triggered / ManualApproval gate required
DEV001Isolated sandboxManualNone

Promotion flow: A → UAT → PAV

Docker & Container Build

Multi-Stage Dockerfile Pattern

FROM mcr.microsoft.com/​dotnet/​aspnet:8.0 AS base
WORKDIR /​app
EXPOSE 8080

FROM mcr.microsoft.com/​dotnet/​sdk:8.0 AS build
WORKDIR /​src
COPY ["src/<Service>/​Host/​Host.csproj", "src/<Service>/​Host/"]
RUN dotnet restore "src/<Service>/​Host/​Host.csproj"
COPY . .
WORKDIR "/​src/​src/<Service>/​Host"
RUN dotnet build "Host.csproj" -c Release -o /​app/​build

FROM build AS publish
RUN dotnet publish "Host.csproj" -c Release -o /​app/​publish

FROM base AS final
WORKDIR /​app
COPY --from=publish /​app/​publish .
ENTRYPOINT ["dotnet", "Host.dll"]

Image Structure per Service

docker/<service-name>/
├── Dockerfile
└── k8s/
    ├── A/
    │   ├── values.yaml
    │   └── appsettings.json    # Confix-encrypted
    ├── UAT/
    └── PAV/

ACR

  • Images via $(ACR) variable, tag = $(Build.SourceBranchName) (git tag, SemVer)
  • Helm chart registry: $(HelmRepoName).azurecr.io (OCI-based)

Helm Deployment Flow

  1. Download Docker artifact from build stage
  2. Copy K8s manifests + values.yaml
  3. Confix Decrypt — dotnet confix decrypt via Azure CLI service connection
  4. Split BffContainer from appsettings.json → appsettings.bff.json
  5. Helm login: helm registry login $(HelmRepoName).azurecr.io
  6. Helm pull: oci://$(HelmRepoName).azurecr.io/​deployment-ng
  7. Helm upgrade --atomic --timeout=600s --create-namespace with environment values
  8. Teams notification

Helm Values Set During Deployment

image.name, image.repository=$(ACR), image.tag=$(Build.SourceBranchName)
env.DEPLOYMENT_ENVIRONMENT, env.DEPLOYMENT_VAULT, env.ASPNETCORE_ENVIRONMENT
env.OTEL_EXPORTER_OTLP_ENDPOINT, env.REMOTE_CONFIGURATION_URL
envSecrets.DEPLOYMENT_DECRYPTIONKEY, envSecrets.DEPLOYMENT_SHAREDSECRET
envSecrets.REMOTE_CONFIGURATION_TOKEN

Health Probes

All services: /​_health/​live (liveness), /​_health/​ready (readiness)

Versioning & Release

  • SemVer: MAJOR.MINOR.PATCH, preview: MAJOR.MINOR.PATCH-preview.N
  • Release tags on master → full deployment pipeline
  • Flow: feature branch → PR → merge → release tag → A (auto) → UAT (approval) → PAV (approval)

Configuration Management

Confix

Encrypts appsettings.json at rest. Pipeline decrypts using $(ConfixDecryptServiceConnectionName).

Azure Key Vault

Secrets via DEPLOYMENT_VAULT + DEPLOYMENT_DECRYPTIONKEY. Managed Identity in production.

Infrastructure as Code

  • Terraform for Azure resources (I_IaC project)
  • Helm for K8s packages
  • ARM Templates for legacy resources
  • CCOE manages foundational infra, quarterly compliance reviews

Observability

ComponentPurpose
OpenTelemetryDistributed tracing, metrics
Elastic APMPerformance monitoring
Structured loggingILogger + Serilog → Elasticsearch
Health checksK8s liveness/​readiness probes

Pipeline Troubleshooting

Build Failures

SymptomLikely CauseResolution
dotnet restore failsNuGet feed auth, version mismatchCheck nuget.config, Directory.Packages.props
Docker build failsMissing COPY files, SDK mismatchCheck paths, global.json
Test failuresFlaky tests, missing infraRun locally, check Squadron containers

Deployment Failures

SymptomLikely CauseResolution
Helm upgrade failsChart not found, values.yaml errorVerify HelmChartVersion, validate YAML
Confix decrypt failsService connection permissionsCheck ConfixDecryptServiceConnectionName
CrashLoopBackOffConfig error, missing secretskubectl logs, verify appsettings
ImagePullBackOffACR auth, tag not foundVerify ACR creds, confirm image push
Timeout (600s)Pod not ready, low resourcesIncrease resources in values.yaml

Pipeline Permission Issues

SymptomLikely CauseResolution
Authorization failedService connection expiredRenew ADO service connections
Pipeline not triggeredTag trigger mismatchVerify trigger in pipeline YAML
Template not foundRepo resource ref wrongCheck resources.repositories branch

Diagnostic Commands

kubectl get pods -n <ns> -l app=<svc>
kubectl logs -n <ns> -l app=<svc> --tail=100
kubectl describe pod <pod> -n <ns>
kubectl rollout status deployment/<svc> -n <ns>
kubectl rollout undo deployment/<svc> -n <ns>
helm list -n <ns>
helm history <release> -n <ns>

Key File Locations

FilePurpose
.devops/​azure-pipelines.release-*.ymlRelease pipelines per service
.devops/​azure-pipelines.test-pr.ymlPR test pipeline
.devops/​templates/​template.*.ymlShared pipeline templates
docker/<service>/​DockerfileDocker build
docker/<service>/​k8s/​{A,UAT,PAV}/​values.yamlHelm values per environment
Directory.Build.propsCentral MSBuild properties
Directory.Packages.propsCentral NuGet package versions
global.json.NET SDK version pinning

Key Pipeline Variables

VariableSourcePurpose
$(ACR)Variable templateAzure Container Registry URL
$(ConfigEnvironment)Variable templateTarget environment name
$(ConfigVault)Variable templateKey Vault name
$(KubernetesServiceConnection)Variable templateK8s service connection
$(ConfixDecryptServiceConnectionName)Variable templateConfix decrypt service connection
$(HelmRepoName)Variable templateHelm OCI registry name
$(TeamsHookUri)Variable templateTeams webhook
$(Build.SourceBranchName)Built-inGit tag / branch name (image tag)