Deploying the same 3-tier application again and again β dev, test, prod β shouldnβt feel like dΓ©jΓ vu every time.
But in many cloud teams, it does.
Manual fixesβ¦ copy-pasted Terraformβ¦ secrets hardcoded inside .tfvarsβ¦
One small change in dev, not updated in prodβ¦
Boom! Configuration drift, broken deployments, security risks.
This hands-on guide shows you exactly how to eliminate all of that using:
β
Terraform Modular Architecture
β
Azure Key Vault for Secure Secrets Management
β
Remote State in Azure Storage
β
GitHub Actions for Fully Automated CI/CD
By the end, youβll be able to deploy dev, test, and prod environments identically, securely, and on autopilot.
π₯ 1. The Problem: Manual Deployments = Drift + Errors + Chaos
Most teams still deploy environments like this:
- Copy old Terraform folder
- Change a few names
- Adjust IPs manually
- Forget a network rule
- Hardcode passwords βfor nowβ π
- Fix mistakes after something breaks
Result?
β Inconsistent infra across environments
β Security breaches due to exposed secrets
β Time wasted troubleshooting
β Zero auditability
β No single source of truth
This hands-on solves exactly this.
π 2. Why This Use Case Matters
Cloud teams today need consistency + speed + security.
Manually managing infra no longer works.
This use case delivers:
π§± Reusable Terraform Modules
Resource Group, VNet, Subnet, NSG, VM β once written, reused forever.
π Zero Secret Sprawl
Passwords and sensitive values stored in Azure Key Vault, pulled directly in Terraform.
π¦ Environment-driven Deployment
All differences (dev/test/prod) live in terraform.tfvars.
π€ GitHub Actions = Fully Automated Deployments
Plan β Validate β Apply β Audit logs β everything automated.
This is production-grade Terraform, not just a tutorial.
π 3. When You Need This Use Case
You need this setup when:
βοΈ Deploying multiple environments
βοΈ Avoiding inconsistent infra
βοΈ Securing all secrets centrally
βοΈ Enabling fast onboarding
βοΈ Needing auditability and governance
βοΈ Running builds from CI/CD pipelines
βοΈ Scaling infra to multiple regions
This architecture grows as your company grows.
π οΈ 4. Prerequisites
- Azure Subscription
- Azure CLI
- Terraform Installed
- Git + GitHub
- Key Vault access
- Optional: GitHub Actions Service Principal
Youβre ready.
π― 5. Challenge Questions (Interview-Level)
These make great DevOps interview questions too:
- How do you avoid copy-paste Terraform for dev/test/prod?
- How do you secure plaintext secrets in Terraform?
- How do you stop network drift between environments?
- How do you enable new developers to deploy infra securely?
- How do you prove that all environments are deployed from the same code?
- How do you roll back a Terraform deployment?
- How do you prevent faulty tfvars from affecting prod?
- How do you design a module for both Linux & Windows VMs?
- How do you deploy identical infra to two regions?
- Why are modules better than plain Terraform scripts?
π§βπ» 6. Complete Hands-On Implementation
Below is the full real-life end-to-end setup.
STEP 1οΈβ£ β Authenticate to Azure & Configure Git
az login
git config --global user.name "yourname"
git config --global user.email "yourmail@example.com"
Initialize GitHub repository:
git init
echo "# azure-3-tier-architecture" >> README.md
git add .
git commit -m "first commit"
git branch -M main
git remote add origin https://github.com/<yourid>/azure-3-tier-architecture.git
git push -u origin main
STEP 2οΈβ£ β Create Backend Resources
LOCATION="eastus"
RG_NAME="tfstate-rg"
STORAGE_NAME="mytfstate12345"
CONTAINER_NAME="tfstate"
KV_NAME="mykeyvault12345"
az group create --name $RG_NAME --location $LOCATION
az storage account create --name $STORAGE_NAME --resource-group $RG_NAME --location $LOCATION --sku Standard_LRS
az storage container create --name $CONTAINER_NAME --account-name $STORAGE_NAME
STEP 3οΈβ£ β Create Key Vault + Secrets
az keyvault create --name $KV_NAME --resource-group $RG_NAME --location $LOCATION
Store secrets
az keyvault secret set --vault-name $KV_NAME --name "vm-username" --value "learning"
az keyvault secret set --vault-name $KV_NAME --name "vm-password" --value "Redhat@12345"
STEP 4οΈβ£ β Create GitHub Service Principal
az ad sp create-for-rbac --name "github-spn" --role="Contributor" --scopes="/subscriptions/<subid>" --sdk-auth
Save JSON output.
Grant Key Vault access:
az role assignment create \
--assignee <clientId> \
--role "Key Vault Secrets User" \
--scope $(az keyvault show --name $KV_NAME --query id -o tsv)
STEP 5οΈβ£ β Create Terraform Structure
terraform/
βββ backend.tf
βββ main.tf
βββ variables.tf
βββ environments/
β βββ dev/terraform.tfvars
β βββ test/terraform.tfvars
β βββ prod/terraform.tfvars
βββ modules/
βββ rg/
βββ vnet/
βββ subnet/
βββ nsg/
βββ vm/
STEP 6οΈβ£ β Add Root Terraform Files
backend.tf
terraform {
backend "azurerm" {
resource_group_name = "tfstate-rg"
storage_account_name = "mytfstate12345"
container_name = "tfstate"
key = "3tier/dev.tfstate"
}
}
variables.tf
variable "location" { type = string }
variable "rg_name" { type = string }
variable "vnet_name" { type = string }
variable "vm_name" { type = string }
main.tf
provider "azurerm" {
features {}
}
module "rg" {
source = "./modules/rg"
name = var.rg_name
location = var.location
}
module "vnet" {
source = "./modules/vnet"
name = var.vnet_name
location = var.location
resource_group_name = module.rg.name
}
module "subnet" {
source = "./modules/subnet"
name = "${var.vnet_name}-subnet"
vnet_name = module.vnet.name
resource_group_name = module.rg.name
nsg_id = module.nsg.id
}
module "nsg" {
source = "./modules/nsg"
name = "${var.vnet_name}-nsg"
location = var.location
resource_group_name = module.rg.name
}
data "azurerm_key_vault" "kv" {
name = "mykeyvault12345"
resource_group_name = "tfstate-rg"
}
data "azurerm_key_vault_secret" "vm_username" {
name = "vm-username"
key_vault_id = data.azurerm_key_vault.kv.id
}
data "azurerm_key_vault_secret" "vm_password" {
name = "vm-password"
key_vault_id = data.azurerm_key_vault.kv.id
}
module "vm" {
source = "./modules/vm"
name = var.vm_name
location = var.location
resource_group_name = module.rg.name
subnet_id = module.subnet.id
admin_username = data.azurerm_key_vault_secret.vm_username.value
admin_password = data.azurerm_key_vault_secret.vm_password.value
}
STEP 7οΈβ£ β Environment Variables
dev
rg_name = "rg-dev"
vnet_name = "vnet-dev"
vm_name = "vm-dev"
location = "eastus"
STEP 8οΈβ£ β Build Modules
(Example: Resource Group)
modules/rg/main.tf
resource "azurerm_resource_group" "rg" {
name = var.name
location = var.location
}
modules/rg/variables.tf
variable "name" { type = string }
variable "location" { type = string }
modules/rg/outputs.tf
output "name" { value = azurerm_resource_group.rg.name }
Repeat similar for vnet, subnet, nsg, vm.
π Final Output
You now have:
βοΈ Modular Terraform
βοΈ Secure secrets with Key Vault
βοΈ Remote state
βοΈ Reusable environments
βοΈ Ready for GitHub Actions automation
This is true enterprise-grade Infrastructure as Code.
β Follow Me for Daily DevOps & Cloud Content
π΅ LinkedIn: @techopsbysonali
π¦ Twitter / X: @techopsbysonali
πΈ Instagram: @techopsbysonali
π Medium: @techopsbysonali
π Dev.to: @techopsbysonali
π Hashnode: techopsbysonali.hashnode.dev
ποΈ Blogger: techopsbysonali.blogspot.com
**
π² Join My WhatsApp Communities
**
π Personalized Guidance: https://wa.me/7620774352
π Latest Updates Group: https://lnkd.in/gVTvmRBa
π Pune Local Meetup Group: https://lnkd.in/gQbKaUeX
Top comments (0)