Appearance
Runbook: Onboard a Repository to the DevSecOps Pipeline
Estimated time: 1–2 hours
Audience: DevOps engineers, team leads
Prerequisites: Azure DevOps Project Administrator access, SonarCloud organization access
Overview
This runbook walks through every step required to connect a new repository or new service to the centralized 16-stage DevSecOps pipeline template.
Repo setup → SonarCloud → Pipeline YAML → ADO pipeline → First runStep 1 — Verify Repository Structure
Before onboarding, confirm the repository contains the expected structure:
Backend (.NET) repositories:
RepoRoot/
├── {ServiceName}.Apis/
│ ├── Dockerfile ← Required
│ └── {ServiceName}.Apis.csproj
├── {ServiceName}.Application/
├── {ServiceName}.Domain/
├── {ServiceName}.Infrastructure/
└── {ServiceName}.Tests/
└── {ServiceName}.Tests.csprojFrontend (Angular) repositories:
RepoRoot/
├── src/
├── angular.json
├── package.json
└── Dockerfile ← Required (for containerised frontends)No Dockerfile = no pipeline
The DevSecOps template requires a Dockerfile to run Stages 8 (Docker build) and 9 (Trivy scan). If your service is library-only (no container), use the libraryOnly: true parameter — but this is rare. Confirm with the DevOps team.
Step 2 — Create the SonarCloud Project
SonarCloud is used in Stage 3 (SAST). Each repository needs its own SonarCloud project.
- Open sonarcloud.io → Organization: microtec
- Click + → Analyze new project
- Choose Azure DevOps → Select the repository
- Configure:
| Field | Value |
|---|---|
| Project Key | microtec_{service-name} (e.g., microtec_hr-service) |
| Display Name | {ServiceName} |
| Language | C# (backend) or TypeScript (frontend) |
| Quality Gate | Microtec ERP Standard |
- Note the Project Key — you will need it in Step 4.
Required Quality Gate
The Microtec ERP Standard Quality Gate must be applied. It enforces:
| Metric | Threshold |
|---|---|
| Coverage on new code | ≥ 80% |
| Duplicated lines | < 5% |
| Maintainability rating | A |
| Reliability rating | A |
| Security rating | A |
| Security hotspots reviewed | 100% |
If the gate does not appear in the dropdown, contact the DevOps team to create it at the organization level.
Step 3 — Configure Gitleaks
Gitleaks runs in Stage 1 to detect hardcoded secrets. The default ruleset covers 800+ patterns. Most repositories work with the default configuration.
If the repository has known false positives (e.g., test fixture files with fake API keys), create a .gitleaks.toml in the repository root:
toml
[allowlist]
description = "Known false positives"
files = ["tests/fixtures/test-secrets.json"]
paths = ["tests/e2e/mock-data/"]
regexes = ["EXAMPLE_ONLY_.*"]Never allowlist real secrets
Allowlisting should only be used for test data with clearly fake values (e.g., sk_test_XXXXXXXXXXXXXXXX). If a real secret is found, rotate it immediately — do not allowlist it.
Step 4 — Create the Pipeline YAML
Create the pipeline entrypoint file in the Devops/ repository (not in the service repository):
Devops/azure/pipelines/{service-name}/deploy-{service-name}.ymlBackend (.NET) example
yaml
# deploy-hr-service.yml
name: $(Build.BuildId)
trigger:
branches:
include:
- main
- stage
- PreProd
- production
paths:
include:
- Platforms/Src/InfrastructureServices/HR/**
pr:
branches:
include:
- main
paths:
include:
- Platforms/Src/InfrastructureServices/HR/**
parameters:
- name: environment
type: string
default: dev
values: [dev, stage, preprod, uat, production]
- name: forceFullPipeline
type: boolean
default: false
extends:
template: ../../templates/devsecops/pipeline-template.yml
parameters:
# ── Required ──────────────────────────────────────────────
serviceName: hr-service
dockerfilePath: Platforms/Src/InfrastructureServices/HR/HR.Apis/Dockerfile
testProjectPath: Platforms/Src/InfrastructureServices/HR/HR.Tests/HR.Tests.csproj
sonarProjectKey: microtec_hr-service
# ── Optional overrides ────────────────────────────────────
environment: ${{ parameters.environment }}
forceFullPipeline: ${{ parameters.forceFullPipeline }}
coverageThreshold: 80 # default; override for bootstrapping
networkProfile: private # 'public' for gateway/keycloak
enableDastScan: true
enableIntegrationTests: false # Set true when Postman collection existsFrontend (Angular) example
yaml
# deploy-apps-hr-frontend.yml
extends:
template: ../../templates/devsecops/pipeline-template.yml
parameters:
serviceName: apps-hr
serviceType: frontend # Switches to npm build instead of dotnet
dockerfilePath: FrontApps/projects/apps-hr/Dockerfile
sonarProjectKey: microtec_apps-hr
environment: ${{ parameters.environment }}
networkProfile: publicAvailable Parameters
| Parameter | Required | Default | Description |
|---|---|---|---|
serviceName | Yes | — | Matches the name in services-config.json |
dockerfilePath | Yes | — | Relative path to Dockerfile from repo root |
sonarProjectKey | Yes | — | SonarCloud project key from Step 2 |
testProjectPath | Backend only | — | Path to the .NET test project |
serviceType | No | backend | backend or frontend |
environment | No | dev | Target environment |
networkProfile | No | private | private or public (ACA profile) |
coverageThreshold | No | 80 | Minimum coverage percentage (Stage 6) |
enableDastScan | No | true | Run OWASP ZAP in Stage 13 |
enableIntegrationTests | No | false | Run Postman/Newman in Stage 12 |
forceFullPipeline | No | false | Skip change detection; run all stages |
Step 5 — Register the Pipeline in Azure DevOps
- In Azure DevOps, go to Pipelines → New Pipeline
- Choose Azure Repos Git (or GitHub, if applicable)
- Select the
Devopsrepository - Choose Existing Azure Pipelines YAML file
- Path:
/azure/pipelines/{service-name}/deploy-{service-name}.yml - Click Save (do not run yet)
- Rename the pipeline:
deploy-{service-name}
Connect to Variable Groups
In the pipeline's Variables → Variable Groups, link:
microtec-erp-devmicrotec-erp-stagemicrotec-erp-preprodmicrotec-erp-uatmicrotec-erp-production
Step 6 — Configure Branch Policies (PRs)
Set up a required status check on the target branch so that PRs cannot merge without passing the pipeline:
- Azure DevOps → Repos → Branches →
main→ Branch Policies - Add Build Validation:
| Field | Value |
|---|---|
| Build pipeline | deploy-{service-name} |
| Trigger | Automatic |
| Policy requirement | Required |
| Build expiration | 12 hours |
| Display name | DevSecOps: {service-name} |
Step 7 — Verify the Pipeline
Trigger the first run manually:
bash
az pipelines run \
--name "deploy-{service-name}" \
--parameters "environment=dev" \
--branch mainMonitor the run. Expected stage progression on a healthy new service:
| Stage | Expected Duration | Common First-Run Issue |
|---|---|---|
| 1. Secret scan | 1–2 min | False positive from test fixtures (see Step 3) |
| 2. Dependency audit | 3–5 min | Missing nuget.config |
| 3. SonarCloud | 3–8 min | Project key mismatch |
| 4. License check | 1–2 min | GPL dependency (must resolve before proceeding) |
| 5–6. Tests + coverage | 2–10 min | Coverage below 80% (use coverageThreshold override for bootstrapping) |
| 7–10. Build + push | 5–15 min | NuGet PAT not in variable group |
| 11. Deploy dev | 3–5 min | Service not in services-config.json |
Step 8 — Add to the Repo Registry
Update Devops/azure/docs/repo-registry.md (or the equivalent registry file) with the new repository entry:
markdown
| {service-name} | {Repository URL} | {Team} | deploy-{service-name} | microtec_{service-name} |Checklist
- [ ] Repository structure verified (Dockerfile present)
- [ ] SonarCloud project created with correct project key
- [ ] Microtec ERP Standard quality gate applied
- [ ] Gitleaks false-positive suppressions configured (if needed)
- [ ] Pipeline YAML created in
Devops/azure/pipelines/{service-name}/ - [ ] Pipeline registered in Azure DevOps and renamed
- [ ] Variable groups linked
- [ ] Branch policy set on
main - [ ] First pipeline run succeeded (all stages green)
- [ ] Repo registry updated
Troubleshooting
| Symptom | Likely Cause | Fix |
|---|---|---|
Stage 1 fails with git-secrets found | Real or false-positive secret | Rotate if real; add to .gitleaks.toml if false positive |
Stage 3 fails Quality Gate failed | Coverage < 80% on new code | Add tests; or use coverageThreshold: 60 temporarily |
Stage 3: Project not found | Wrong sonarProjectKey | Verify key in SonarCloud project settings |
| Stage 9 fails with CRITICAL CVE | Outdated base image | Use mcr.microsoft.com/dotnet/aspnet:8.0 and run docker pull to get latest patch |
Stage 11 fails ContainerApp not found | Service not in services-config.json | Add entry; re-run pipeline |