Skip to content

10.4 Virtual Machines Setup

Overview

This section covers the creation of two Linux virtual machines running Ubuntu 22.04 LTS. The VMs are configured with password authentication and will host Nginx web servers.

Random Password Generation

Terraform Configuration

resource "random_password" "example" {
  length  = 16
  special = true
  lower   = true
  upper   = true
  numeric = true
}

Password Requirements

  • Length: 16 characters
  • Complexity: Includes uppercase, lowercase, numbers, and special characters
  • Security: Randomly generated, meets Azure password requirements

Usage

The password is used as a fallback if no password is provided via variables:

admin_password = coalesce(var.password, random_password.example.result)

Logic: If var.password is empty, use the randomly generated password.

Virtual Machine Configuration

Terraform Configuration

resource "azurerm_linux_virtual_machine" "example" {
  count                 = 2
  name                  = "${var.virtual_machine_name}${count.index}"
  location              = azurerm_resource_group.example.location
  resource_group_name   = azurerm_resource_group.example.name
  network_interface_ids = [azurerm_network_interface.example[count.index].id]
  size                  = var.virtual_machine_size

  os_disk {
    name                 = "${var.disk_name}${count.index}"
    caching              = "ReadWrite"
    storage_account_type = var.redundancy_type
  }

  source_image_reference {
    publisher = "Canonical"
    offer     = "0001-com-ubuntu-server-jammy"
    sku       = "22_04-lts-gen2"
    version   = "latest"
  }

  admin_username                  = var.username
  admin_password                  = coalesce(var.password, random_password.example.result)
  disable_password_authentication = false
}

Configuration Breakdown

Basic Properties

Property Value Description
count 2 Creates two VMs (VM 0 and VM 1)
name test-vm0, test-vm1 Indexed naming
location From resource group Same region as other resources
resource_group_name From resource group Logical container
network_interface_ids Array with one NIC Connects VM to network
size Standard_B2s VM size/SKU

VM Size: Standard_B2s

Specifications: - vCPUs: 2 - RAM: 4 GB - Temp Storage: 8 GB - Max Data Disks: 4 - Max NICs: 2 - Cost: ~$30-40/month (varies by region)

Use Case: Development, testing, low-traffic web servers

Alternatives: - Standard_B1s: 1 vCPU, 1 GB RAM (cheaper, minimal workloads) - Standard_B2ms: 2 vCPU, 8 GB RAM (more memory) - Standard_D2s_v3: 2 vCPU, 8 GB RAM (production workloads)

OS Disk Configuration

os_disk {
  name                 = "${var.disk_name}${count.index}"
  caching              = "ReadWrite"
  storage_account_type = var.redundancy_type
}
Property Value Description
name test-disk0, test-disk1 Disk identifier
caching ReadWrite Caches reads and writes
storage_account_type Standard_LRS Locally redundant storage

Storage Types

Type Performance Redundancy Use Case Cost
Standard_LRS HDD Local Dev/Test $
StandardSSD_LRS SSD Local General purpose $$
Premium_LRS Premium SSD Local Production $$$
Premium_ZRS Premium SSD Zone High availability $$$$

Caching Options

Option Behavior Use Case
None No caching Write-intensive workloads
ReadOnly Cache reads Read-heavy workloads
ReadWrite Cache both General purpose (default)

Operating System Image

Image Reference

source_image_reference {
  publisher = "Canonical"
  offer     = "0001-com-ubuntu-server-jammy"
  sku       = "22_04-lts-gen2"
  version   = "latest"
}

Image Details

  • Publisher: Canonical (Ubuntu maintainer)
  • Offer: Ubuntu Server 22.04 LTS (Jammy Jellyfish)
  • SKU: 22.04 LTS Generation 2
  • Version: Latest available

Why Ubuntu 22.04 LTS?

  • LTS: Long-term support until 2027
  • Stability: Production-ready
  • Package Support: Extensive repository
  • Gen2: Better performance, UEFI boot

Finding Other Images

# List publishers
az vm image list-publishers --location eastus --output table

# List offers from Canonical
az vm image list-offers --publisher Canonical --location eastus --output table

# List SKUs
az vm image list-skus \
  --publisher Canonical \
  --offer 0001-com-ubuntu-server-jammy \
  --location eastus \
  --output table

# List versions
az vm image list \
  --publisher Canonical \
  --offer 0001-com-ubuntu-server-jammy \
  --sku 22_04-lts-gen2 \
  --location eastus \
  --all \
  --output table

Authentication Configuration

Password Authentication

admin_username                  = var.username
admin_password                  = coalesce(var.password, random_password.example.result)
disable_password_authentication = false
Property Value Description
admin_username azureadmin Admin account name
admin_password Variable or random Secure password
disable_password_authentication false Allows password login

Security Considerations

⚠️ Password Authentication: Enabled for simplicity, but consider SSH keys for production.

Better Approach for Production:

resource "azurerm_linux_virtual_machine" "example" {
  # ... other configuration ...

  admin_username                  = var.username
  disable_password_authentication = true

  admin_ssh_key {
    username   = var.username
    public_key = file("~/.ssh/id_rsa.pub")
  }
}

Variables

VM Configuration Variables

variable "username" {
  type        = string
  default     = "azureadmin"
  description = "The username for the local account that will be created on the new VM."
}

variable "password" {
  type        = string
  default     = ""
  description = "The password for the local account that will be created on the new VM."
}

variable "virtual_machine_name" {
  type        = string
  default     = "test-vm"
  description = "Name of the Virtual Machine."
}

variable "virtual_machine_size" {
  type        = string
  default     = "Standard_B2s"
  description = "Size or SKU of the Virtual Machine."
}

Storage Variables

variable "disk_name" {
  type        = string
  default     = "test-disk"
  description = "Name of the OS disk of the Virtual Machine."
}

variable "redundancy_type" {
  type        = string
  default     = "Standard_LRS"
  description = "Storage redundancy type of the OS disk."
}

VM Lifecycle

Creation Process

1. Terraform creates random password
2. Terraform creates NICs
3. Terraform creates VMs with NICs attached
4. Azure provisions VMs
5. VMs boot and become available
6. VM extensions install (next section)

Resource Dependencies

random_password.example
        ↓
azurerm_network_interface.example[0,1]
        ↓
azurerm_linux_virtual_machine.example[0,1]
        ↓
azurerm_virtual_machine_extension.example[0,1]

Azure CLI Verification

List VMs

az vm list \
  --resource-group <resource-group-name> \
  --output table

Show VM Details

az vm show \
  --resource-group <resource-group-name> \
  --name test-vm0 \
  --output json

Get VM Status

az vm get-instance-view \
  --resource-group <resource-group-name> \
  --name test-vm0 \
  --query instanceView.statuses \
  --output table

Get Private IP Address

az vm list-ip-addresses \
  --resource-group <resource-group-name> \
  --name test-vm0 \
  --output table

Retrieve Generated Password

# Using Terraform
terraform output vm_password

# Note: Output is marked as sensitive, use:
terraform output -raw vm_password

VM Management Commands

Start VM

az vm start \
  --resource-group <resource-group-name> \
  --name test-vm0

Stop VM (Deallocate)

az vm deallocate \
  --resource-group <resource-group-name> \
  --name test-vm0

Restart VM

az vm restart \
  --resource-group <resource-group-name> \
  --name test-vm0

Resize VM

# List available sizes
az vm list-sizes \
  --location eastus \
  --output table

# Resize VM
az vm resize \
  --resource-group <resource-group-name> \
  --name test-vm0 \
  --size Standard_B2ms

Advanced Configurations

Custom Data (Cloud-Init)

resource "azurerm_linux_virtual_machine" "example" {
  # ... other configuration ...

  custom_data = base64encode(<<-EOF
    #!/bin/bash
    apt-get update
    apt-get install -y nginx
    echo "Hello from $(hostname)" > /var/www/html/index.html
    systemctl restart nginx
  EOF
  )
}

Availability Set

For better availability without load balancer:

resource "azurerm_availability_set" "example" {
  name                = "test-avset"
  location            = azurerm_resource_group.example.location
  resource_group_name = azurerm_resource_group.example.name
}

resource "azurerm_linux_virtual_machine" "example" {
  # ... other configuration ...
  availability_set_id = azurerm_availability_set.example.id
}

Managed Identity

For Azure service authentication:

resource "azurerm_linux_virtual_machine" "example" {
  # ... other configuration ...

  identity {
    type = "SystemAssigned"
  }
}

Cost Optimization

Development/Testing

  • Use B-series (burstable) VMs
  • Stop VMs when not in use (deallocate)
  • Use Standard_LRS storage

Production

  • Use Reserved Instances (1-3 year commitment)
  • Right-size VMs based on monitoring
  • Use Azure Hybrid Benefit if you have Windows licenses

Cost Calculation

VM Cost = (VM Size Cost + Storage Cost) × Hours Running
Example: Standard_B2s in East US
- VM: ~$0.0416/hour
- Storage (30GB Standard_LRS): ~$1.54/month
- Total: ~$32/month (if running 24/7)

Best Practices

  1. Use SSH Keys: More secure than passwords for production
  2. Enable Boot Diagnostics: Helps troubleshoot boot issues
  3. Use Managed Disks: Simpler management, better reliability
  4. Tag Resources: For cost tracking and organization
  5. Regular Updates: Keep OS and packages updated
  6. Monitoring: Enable Azure Monitor for VMs
  7. Backup: Configure Azure Backup for production VMs

Next Steps

Proceed to 10.5-Load-Balancer-Setup.md to configure the load balancer that distributes traffic across these VMs.