Production-grade Infrastructure-as-Code for deploying a resilient, auto-scaling, fully-monitored web infrastructure on Microsoft Azure.
Azure Front Door (CDN + WAF)
│
▼
Azure Application Gateway (SSL/TLS, WAF v2)
│
▼
Azure Load Balancer (Standard, HA)
│
▼
┌─────────────────────────────────┐
│ VM Scale Set (Ubuntu 24.04) │
│ Auto-scale: 1–10 instances │
│ Premium SSD, health probes │
└─────────────────────────────────┘
│
├── Azure Monitor (Metrics, Logs, Alerts)
├── Azure Key Vault (Secrets, SSH keys)
├── Azure Bastion (Secure admin access)
├── Azure Backup (Daily retention)
└── Log Analytics + App Insights
| Feature | Implementation |
|---|---|
| Infrastructure as Code | Terraform (HCL) with modular, reusable components |
| Configuration Management | Ansible — Nginx deployment, security hardening |
| CI/CD | GitHub Actions — security scan, plan, multi-env deploy |
| Auto-scaling | CPU-based scale-out (>75%) and scale-in (<30%), 1–10 instances |
| High Availability | Load Balancer, health probes, fault domains |
| Security | SSH key auth, Key Vault, NSG with least-privilege, WAF-ready |
| Monitoring | Azure Monitor, Application Insights, Log Analytics, metric alerts |
| Backup & DR | Recovery Services Vault, daily backup with monthly/yearly retention |
| Secure Access | Azure Bastion — no public VM endpoints |
| Encryption | Infrastructure double encryption, TLS 1.2+, HTTPS-only |
| Multi-environment | Dev → Staging → Production with manual approval gates |
| Technology | Purpose | Version |
|---|---|---|
| Terraform | Cloud provisioning | ≥ 1.6 |
| Ansible | Config management | Latest |
| Azure | Cloud provider | — |
| Ubuntu | VM OS | 24.04 LTS (Noble Numbat) |
| Nginx | Web server | Latest stable |
| GitHub Actions | CI/CD | — |
- Terraform ≥ 1.6
- Azure CLI
- Azure subscription with Owner or Contributor access
az login
az account set --subscription "<SUBSCRIPTION_ID>"Replace YOUR_TFSTATE_RG, YOUR_TFSTATE_SA, and YOUR_LOCATION with your own values:
az group create --name YOUR_TFSTATE_RG --location YOUR_LOCATION
az storage account create --name YOUR_TFSTATE_SA --resource-group YOUR_TFSTATE_RG --sku Standard_LRS --allow-blob-public-access false
az storage container create --name tfstate --account-name YOUR_TFSTATE_SAThen update providers.tf with the names you chose.
# Initialize
make init
# Preview changes
make plan ENVIRONMENT=dev
# Apply
make apply ENVIRONMENT=devOr step by step:
cd terraform
terraform init
TF_VAR_environment=dev TF_VAR_branch_name=$(git rev-parse --abbrev-ref HEAD) terraform plan
TF_VAR_environment=dev TF_VAR_branch_name=$(git rev-parse --abbrev-ref HEAD) terraform applyAfter deployment, the Load Balancer public IP is shown as output:
make output
# Or: cd terraform && terraform outputVisit http://<LOAD_BALANCER_IP> in your browser.
├── .github/workflows/ # CI/CD pipeline (security scan, plan, apply)
├── ansible/
│ ├── setup.sh # VM bootstrap script
│ ├── playbooks/
│ │ ├── install-nginx.yml # Main playbook
│ │ └── roles/nginx-setup/
│ │ ├── tasks/ # Role tasks
│ │ ├── handlers/ # Service management
│ │ └── templates/ # Jinja2 templates
├── terraform/
│ ├── main.tf # Root configuration
│ ├── providers.tf # Provider + backend config
│ ├── variables.tf # Input variables
│ ├── outputs.tf # Output values
│ ├── locals.tf # Local values + tags
│ └── modules/
│ ├── resource_group/ # Azure Resource Group
│ ├── vnet_and_subnet/ # VNet + subnets
│ ├── nsg/ # Network Security Group
│ ├── storage_account/ # Encrypted storage
│ ├── key_vault/ # Secrets management
│ ├── lb_and_pip/ # Load Balancer + Public IP
│ ├── vmss/ # VM Scale Set (auto-scaling)
│ ├── vmss_extension/ # VM extensions (Ansible, Azure Monitor)
│ ├── bastion/ # Azure Bastion host
│ ├── monitoring/ # Log Analytics, App Insights, Alerts
│ └── backup/ # Recovery Services Vault
├── Makefile # Developer automation
├── .pre-commit-config.yaml # Pre-commit hooks
├── .tflint.hcl # TFLint config
└── README.md
| Environment | Branch Trigger | Approval Required | Purpose |
|---|---|---|---|
| dev | dev, main, feature/* |
No | Development & testing |
| staging | main |
No | Pre-production validation |
| production | main (tag) |
Manual | Production workloads |
- Zero hardcoded secrets — All passwords generated at runtime or stored in Key Vault
- SSH key authentication — Disabled password auth on all VMs
- Network isolation — No public IPs on VMs; Bastion-only access
- Encryption at rest — Infrastructure double encryption on storage
- Encryption in transit — TLS 1.2+ enforced, HTTPS-only
- Security scanning — Checkov + Trivy on every commit
- RBAC — Key Vault uses Azure RBAC; least-privilege NSG rules
| Alert | Condition | Action |
|---|---|---|
| High CPU | > 80% for 5 minutes | Email notification |
| Low CPU | < 20% for 5 minutes | Email notification (idle detection) |
| Unhealthy Hosts | Any VM unhealthy | Email notification |
| Auto-scale events | Scale-in/out actions | Logged to Log Analytics |
| Feature | Savings |
|---|---|
| Auto-scaling | Scale to 1 instance at low traffic |
| Premium SSD | Only for OS disk (64 GB) |
| Reserved instances | Recommended for 1+ year commitment |
| Dev/Test | Use smaller SKUs (Standard_B2s) in dev |
| Azure Hybrid Benefit | Use existing Windows Server licenses |
Please read CONTRIBUTING.md for details on the contribution workflow.
Distributed under the MIT License.
Built with ❤️ using Terraform, Ansible, and Azure. Inspired by production infrastructure patterns at scale.