Deploying infrastructure shouldn't be a copy-paste headache. Here's how to make your Azure VM setup repeatable and clean using Terraform.
When I started using Terraform to manage infrastructure on Azure, one thing became clear very quickly: writing the same Terraform config every time gets old fast.
In this blog, I'm going to show you how to create an Azure Virtual Machine using a reusable Terraform script. By the end of this post, you'll have a modular setup you can use across projects—without rewriting the whole thing each time.
What You’ll Need
Before we begin, make sure you’ve got the following:
- An Azure account
- Terraform installed
- Azure CLI installed and logged in (
az login
)
Step 1: Create the Folder Structure
Let’s keep things clean.
mkdir terraform-azure-vm
cd terraform-azure-vm
mkdir modules
mkdir modules/vm
We’ll keep our reusable VM code inside modules/vm
and the environment-specific code in the root folder.
Step 2: Build the Reusable VM Module
Inside modules/vm/
, create these files:
main.tf
resource "azurerm_network_interface" "nic" {
name = "${var.vm_name}-nic"
location = var.location
resource_group_name = var.resource_group_name
ip_configuration {
name = "internal"
subnet_id = var.subnet_id
private_ip_address_allocation = "Dynamic"
}
}
resource "azurerm_windows_virtual_machine" "vm" {
name = var.vm_name
resource_group_name = var.resource_group_name
location = var.location
size = var.vm_size
admin_username = var.admin_username
admin_password = var.admin_password
network_interface_ids = [azurerm_network_interface.nic.id]
os_disk {
caching = "ReadWrite"
storage_account_type = "Standard_LRS"
}
source_image_reference {
publisher = "MicrosoftWindowsServer"
offer = "WindowsServer"
sku = "2019-Datacenter"
version = "latest"
}
}
variables.tf
variable "vm_name" {}
variable "resource_group_name" {}
variable "location" {}
variable "subnet_id" {}
variable "vm_size" {}
variable "admin_username" {}
variable "admin_password" {}
Step 3: Use the Module in Your Environment
Go to the root folder and create:
main.tf
provider "azurerm" {
features {}
}
module "vm" {
source = "./modules/vm"
vm_name = "dev-vm"
resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
subnet_id = azurerm_subnet.subnet.id
vm_size = "Standard_B1s"
admin_username = "azureuser"
admin_password = "MySecureP@ssw0rd123"
}
resource "azurerm_resource_group" "rg" {
name = "dev-rg"
location = "East US"
}
resource "azurerm_virtual_network" "vnet" {
name = "dev-vnet"
address_space = ["10.0.0.0/16"]
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
}
resource "azurerm_subnet" "subnet" {
name = "dev-subnet"
resource_group_name = azurerm_resource_group.rg.name
virtual_network_name = azurerm_virtual_network.vnet.name
address_prefixes = ["10.0.1.0/24"]
}
Step 4: Don’t Forget the Backend (Optional but recommended)
For team use or remote state management, configure a backend.tf
file using Azure Storage Account.
Step 5: Deploy It!
terraform init
terraform plan
terraform apply
Done! Your Azure VM is up and running.
Wrapping Up
With this modular approach, the next time you need a VM? Just tweak a few inputs. No more rewriting long scripts or fighting with syntax every time.
Let me know in the comments if you'd like to see a Linux version, auto-shutdown scripts, or cost-saving tricks next!
Happy Terraforming!