Engineering case study
Azure · Terraform · Ansible
EpicBook: Dual-Pipeline Deployment with Azure DevOps
A pair of Azure DevOps pipelines that separates infrastructure provisioning from application configuration, handing dynamic VM and database outputs from Terraform to Ansible without committing environment values or secrets.
2
linked delivery pipelines
2
runtime outputs handed off
3
Ansible roles
7d
database backup retention
The problem
What needed to be solved
The VM address and private database hostname are created at infrastructure runtime, while passwords and SSH keys must remain outside Git. The application deployment needed those values without duplicating infrastructure logic or relying on manual copy-and-paste.
My role
What I owned
I designed and implemented the Terraform infrastructure, Azure DevOps artifact contract, runtime inventory generation, and Ansible roles for host preparation, Nginx configuration, database initialization, and PM2 application management.
Architecture
How the system fits together
Infra commit
Triggers Azure Pipeline
Terraform
VNet, VM, private MySQL, DNS
Output artifact
VM IP + database host
App pipeline
Secrets and SSH key injected
Ansible
Common, Nginx, EpicBook roles
Verification
HTTP check through Nginx
Key decisions
Engineering choices and trade-offs
Infrastructure and configuration have separate lifecycles
Terraform owns cloud resources; Ansible owns operating-system and application state. Each repository has one clear responsibility.
A small artifact is the handoff contract
Stage one publishes only the generated VM public IP and MySQL hostname. Stage two consumes them to create inventory and runtime configuration.
Secrets enter only at pipeline runtime
The database password comes from an Azure DevOps variable group, while SSH keys come from Secure Files. Neither is committed to either repository.
Database initialization is guarded
Ansible checks whether tables exist before applying the schema and seed data, allowing repeat runs without blindly reseeding the database.
Pipeline snapshot
Terraform outputs become dynamic Ansible inventory
The downstream pipeline downloads the infrastructure artifact, promotes its values to pipeline variables, and builds the deployment inventory at runtime.
Inspect the source file- download: infra
artifact: infra-outputs
- script: |
source "$(Pipeline.Workspace)/infra/infra-outputs/infra-outputs.env"
echo "##vso[task.setvariable variable=vmPublicIp]$vmPublicIp"
echo "##vso[task.setvariable variable=db_host]$db_host"
- script: |
ansible-playbook site.yml \
-e "db_host=$(db_host)" \
-e "db_password=$(db_password)" Measurable outcome
What the implementation demonstrates
- Two independently maintained pipelines are connected through a stable two-value output contract.
- Three Ansible roles take a fresh VM from base configuration through reverse proxy and application deployment.
- The Azure MySQL server uses private VNet access and seven-day backup retention, while application database traffic requires TLS.
- Every deployment concludes with an automated HTTP request against the generated VM address instead of relying on a manual spot check.