DMI Internship
Production-Grade Deployment — Terraform + Ansible Roles (Azure)
Deployed the EpicBook full-stack web application on Azure using Terraform for infrastructure and a three-role Ansible architecture — common (system baseline and SSH hardening), nginx (reverse proxy configuration via Jinja2 template), and epicbook (MySQL provisioning, Node.js, PM2, schema seeding, and application deployment).

Overview
This project moves from a flat Ansible playbook to a production-grade role architecture — the pattern real teams use when configuration management needs to be modular, reusable, and maintainable across environments. Terraform provisions the Azure infrastructure, and three Ansible roles handle the full application stack in a clean, ordered sequence: system baseline first, then web server configuration, then application deployment.
The application itself is not a static site. EpicBook is a full-stack Node.js web application backed by MySQL, served behind an Nginx reverse proxy — a more realistic deployment target than a flat HTML directory.
Problem
Flat Ansible playbooks work for simple deployments. They become unmanageable when the deployment involves multiple concerns — system hardening, web server configuration, application runtime, database provisioning, and process management — each with its own tasks, templates, and handlers. The challenge was to structure the automation so that each concern is isolated, each role is independently testable, and the whole deployment is idempotent: safe to re-run without unintended side effects.
Architecture
Terraform provisions the full Azure environment from code:
- Resource Group —
rg-epicbook-prod - Virtual Network —
10.0.0.0/16with Subnet10.0.1.0/24 - NSG — inbound TCP 22 and TCP 80 only
- Static Public IP + NIC — associated to the VM
- Ubuntu 22.04 VM (
Standard_B2ats_v2) — key-based SSH, password authentication disabled - Two Terraform outputs —
public_ipandadmin_user, consumed directly by Ansible inventory
Ansible runs three roles in sequence against the provisioned host:
Role: common
apt update + dist-upgrade
Install baseline packages (git, curl, unzip, python3-pip)
Disable root SSH login → handler: restart ssh
Disable password auth → handler: restart ssh
Role: nginx
Install Nginx
Deploy epicbook.conf from Jinja2 template → reverse proxy port 80 to app_port
Enable site (symlink sites-available → sites-enabled)
Remove default Nginx site
Ensure Nginx started and enabled → handler: reload nginx
Role: epicbook
Install MySQL, ensure started and enabled
Install Node.js 20 via NodeSource
Install PM2 globally
Clone https://github.com/pravinmishraaws/theepicbook to app_dest
Set www-data ownership recursively
npm install dependencies
Create MySQL database, import schema, import seed data (guarded with creates:)
Deploy config.json from Jinja2 template
Start application with PM2 → handler: reload nginx
Traffic flow: Browser → Nginx :80 → proxy_pass → Node.js/PM2 :8080
Group Variables
All role-shared configuration lives in group_vars/web.yml:
app_repo: https://github.com/pravinmishraaws/theepicbook.git
app_dest: /var/www/epicbook
app_user: www-data
app_port: 8080
db_name: bookstore
db_user: root
Variables are referenced across roles via Jinja2 ({{ app_dest }}, {{ app_port }}), keeping the roles DRY — changing the deployment path or port requires editing one file, not hunting through tasks.
Technologies Used
- Terraform — Infrastructure as Code, Azure provider
~> 3.100 - Ansible — Role-based configuration management and deployment automation
- Azure — Resource Group, VNet, NSG, Public IP, NIC, Ubuntu 22.04 VM
- Nginx — Reverse proxy: Jinja2-templated site config, port 80 → app_port
- Node.js 20 + PM2 — Application runtime installed via NodeSource; PM2 manages the process
- MySQL — Database provisioned, schema applied, seed data imported via Ansible
- GitHub Actions — CI: Terraform format check and Ansible syntax check on every push
Key Engineering Decisions
- Role boundaries are enforced —
commonhandles OS concerns,nginxhandles web server concerns,epicbookhandles application concerns. No task crosses a role boundary. - Handlers are role-scoped — each role defines only the handlers it owns. The
nginxrole reloads Nginx; thecommonrole restarts SSH. Neither role reaches into the other. - Idempotency for database operations — schema and seed imports are guarded with
creates: /tmp/epicbook_schema_loaded. The import runs once and never again, regardless of how many times the playbook is re-run. - Jinja2 templates for config injection —
epicbook.conf.j2rendersapp_portfromgroup_varsinto the Nginx site config.config.json.j2renders database connection details. Environment-specific values never appear as literals in tasks. - SSH hardening in common — disabling root login and password authentication is a baseline control, not optional. It runs on every host before any application layer is touched.
- Outputs expose what Ansible needs — Terraform outputs both
public_ipandadmin_user, making the handoff from infrastructure to configuration explicit and auditable.
Results
Terraform provisioned the full Azure environment in a single terraform apply. Ansible ran all three roles without failures. The EpicBook application loaded correctly in the browser via http://<public_ip>.
A second playbook run confirmed idempotency — tasks that had nothing to change returned ok or unchanged, with no unintended modifications to the running system.
Key Learnings
The step from a flat playbook to roles is not cosmetic. Roles enforce a discipline that flat playbooks allow you to avoid: each concern must be defined in isolation, handlers must be scoped correctly, and shared configuration must be centralised in group variables rather than duplicated across tasks.
Idempotency matters more than it appears on first run. A deployment that works once is useful. A deployment that can be re-run at any time — to verify state, apply a change, or recover from drift — is the foundation of reliable infrastructure operations. The creates: guards on database imports are a small detail with a significant operational consequence: they make the playbook safe to run against a live system without destroying data.