Azure DevOps Pipelines
All pipelines (pipelines/*.yml) run on the truenas-dr-pool agent pool — the
self-hosted agent described in
Self-Hosted Azure DevOps Agent — since every one of them
needs to reach lab-only infrastructure a Microsoft-hosted agent never could.
| Pipeline | Trigger | What it does |
|---|---|---|
terraform.yml |
Manual | Start infra, then plan/apply base and compute Terraform stacks, stage by stage |
power-state.yml |
Manual | Start/stop any single server (vcsa, dke-mgmt, esxid, esxid2) or all of them, in parallel |
backup.yml |
Daily cron (03:00) | Runs ansible/backup-playbook.yml — NAS/UniFi/pfSense/TrueNAS-DR config backups |
ssl-renew.yml |
— | Certificate renewal, paired with pfSense's ACME setup |
recording-cleanup.yml |
Daily cron (01:00) | Prunes old camera/NVR recordings via ansible/recording_cleanup.yml |
openvpn-azure-dns-A-record.yml |
Daily cron (01:00) | Keeps openvpn.devhome.cloud's A record pointed at the home connection's current public IP |
The make-target pattern
terraform.yml and power-state.yml don't call ansible-playbook/terraform
directly — they shell out to Makefile targets (make start-dke-mgmt, make
tf-plan-base, make tf-apply-compute, etc.), keeping the actual command construction
(inventory paths, working directories, flags) in one place instead of duplicated across
every pipeline YAML. power-state.yml's server/power_state pipeline parameters
compose directly into a Make target name ("${power_state}-${server}"), so adding a
new manageable server is a Makefile change, not a pipeline-YAML change.
power-state.yml -> all and terraform.yml's StartInfra stage both run their
per-server Make targets in parallel (backgrounded with &, then wait) — starting
vcsa, dke-mgmt, and both ESXi hosts concurrently rather than serially, which
matters when the whole lab is coming back from a full power-down.
Ansible-specific pipeline settings worth knowing
ANSIBLE_HOST_KEY_CHECKING: 'false'everywhere — hosts get rebuilt/reinstalled often enough that host-key churn is expected, not something worth failing a pipeline over.failOnStdErr: falseon theAnsible@0task — Ansible prints[WARNING]s (group-name characters, Python interpreter discovery) to stderr even on a fully successful run; the task's actual return code is what determines success, not the presence of stderr output.- Inventory is always the repo's
ansible/hostsfile (DNS names, current users) — never inline IPs in a pipeline, which is exactly what went stale across the network redesign.
Terraform-specific pipeline settings
terraform.yml exposes every TF_VAR_*/ARM_* credential from a pipeline variable
group (terraform-secrets) as job environment variables — this is the one place
credentials flow through ADO's own variable-group secrets rather than a runtime Vault
lookup, since Terraform itself needs ARM_* present in the environment before it can
even reach the vault provider to pull everything else. Compare with
Vault Consumers for how every other tool authenticates.