🚀Day 4 – Automate Static Website Deployment Using Terraform
Azure Zero to Hero Series

Welcome to Day 4 of the Azure Zero to Hero series!
Today’s focus is on Infrastructure as Code (IaC) - Terraform.
You’ll learn:
Automate deployments
Version control your infrastructure
Reusing terraform templates
Step1: Install and configure Azure CLI
az login
Step2: Create a service principal
az ad sp create-for-rbac --name "terraform-sp" --role="Contributor" --scopes="/subscriptions/xx" --sdk-auth
Note: In place of xx in the above command replace your subscription id
Step3: Create terraform files
Open gitbash
mkdir createvm_azure
cd createvm_azure
touch cloud-init.txt variables.tf provider.tf main.tf terraform.tfvars outputs.tf
vi cloud-init.txt
#cloud-config
package_update: true
package_upgrade: true
packages:
- nginx
- wget
- unzip
runcmd:
- systemctl enable nginx
- systemctl start nginx
- wget -O /tmp/lugx_temp.zip https://templatemo.com/download/templatemo_589_lugx_gaming
- unzip -q /tmp/lugx_temp.zip -d /var/www/
- mv /var/www/templatemo_589_lugx_gaming/* /var/www/html/
- rm -rf /tmp/lugx_temp.zip /var/www/templatemo_589_lugx_gaming
- chown -R www-data:www-data /var/www/html
- chmod -R 755 /var/www/html
- systemctl restart nginx
vi variables.tf
variable "subscription_id" {
type = string
description = "Azure Subscription ID"
}
variable "location" {
type = string
description = "Azure Region"
default = "eastus"
}
variable "resource_group" {
type = string
description = "Azure resource group name"
}
variable "vnet_name" {
type = string
description = "Azure Virtual Network Name"
}
variable "subnet_name" {
type = string
description = "Azure Subnet Name"
}
variable "vm_name" {
type = string
description = "Azure Virtual Machine Name"
}
variable "vm_username" {
type = string
description = "Admin Username for the Virtual Machine"
}
variable "vm_password" {
type = string
description = "Admin Password for the Virtual Machine"
sensitive = true
}
variable "custom_data_file" {
type = string
description = "Path to custom data file"
default = "cloud-init.txt"
}
vi provider.tf
provider "azurerm" {
features {}
subscription_id = var.subscription_id
}
vi main.tf
resource "azurerm_resource_group" "main" {
name = var.resource_group
location = var.location
}
resource "azurerm_virtual_network" "main" {
name = var.vnet_name
address_space = ["10.0.0.0/16"]
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
}
resource "azurerm_subnet" "main" {
name = var.subnet_name
resource_group_name = azurerm_resource_group.main.name
virtual_network_name = azurerm_virtual_network.main.name
address_prefixes = ["10.0.0.0/24"]
}
resource "azurerm_network_security_group" "main" {
name = "${var.vm_name}-nsg"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
}
resource "azurerm_network_security_rule" "allow_ssh" {
name = "allow-ssh"
priority = 100
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "22"
source_address_prefix = "*"
destination_address_prefix = "*"
network_security_group_name = azurerm_network_security_group.main.name
resource_group_name = azurerm_resource_group.main.name
}
resource "azurerm_network_security_rule" "allow_http" {
name = "allow-http"
priority = 200
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "80"
source_address_prefix = "*"
destination_address_prefix = "*"
network_security_group_name = azurerm_network_security_group.main.name
resource_group_name = azurerm_resource_group.main.name
}
resource "azurerm_public_ip" "main" {
name = "${var.vm_name}-pip"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
allocation_method = "Static"
sku = "Standard"
}
resource "azurerm_network_interface" "main" {
name = "${var.vm_name}-nic"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
ip_configuration {
name = "internal"
subnet_id = azurerm_subnet.main.id
private_ip_address_allocation = "Dynamic"
public_ip_address_id = azurerm_public_ip.main.id
}
}
resource "azurerm_network_interface_security_group_association" "example" {
network_interface_id = azurerm_network_interface.main.id
network_security_group_id = azurerm_network_security_group.main.id
}
resource "azurerm_linux_virtual_machine" "main" {
name = var.vm_name
resource_group_name = azurerm_resource_group.main.name
location = azurerm_resource_group.main.location
size = "Standard_B1s"
admin_username = var.vm_username
admin_password = var.vm_password
disable_password_authentication = false
network_interface_ids = [
azurerm_network_interface.main.id,
]
custom_data = base64encode(file(var.custom_data_file))
os_disk {
caching = "ReadWrite"
storage_account_type = "Standard_LRS"
name = "${var.vm_name}-osdisk"
}
source_image_reference {
publisher = "Canonical"
offer = "0001-com-ubuntu-server-jammy"
sku = "22_04-lts"
version = "latest"
}
}
vi terraform.tfvars
subscription_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
location = "eastus"
resource_group = "web-rg"
vnet_name = "web-vnet"
subnet_name = "web-subnet"
vm_name = "testservervm"
vm_username = "testuser"
vm_password = "SecUrePwd@Az!$#"
custom_data_file = "cloud-init.txt"
vi outputs.tf
output "vm_public_ip" {
description = "Public IP address of the virtual machine"
value = azurerm_public_ip.main.ip_address
}
output "ssh_username" {
value = var.vm_username
}
output "web_url" {
value = "http://${azurerm_public_ip.main.ip_address}"
}
# Initialize
terraform init
# Validate
terraform validate
# Plan
terraform plan
# Apply
terraform apply -auto-approve
🧠 Tip
If you get any error related to vm sizes, check with bellow command
az vm list-sizes --location eastus -o table

Access the url on browser

Step4: Clean Up Resources
terraform destroy -auto-approve


