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:
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¶
Show VM Details¶
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¶
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¶
Stop VM (Deallocate)¶
Restart VM¶
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¶
- Use SSH Keys: More secure than passwords for production
- Enable Boot Diagnostics: Helps troubleshoot boot issues
- Use Managed Disks: Simpler management, better reliability
- Tag Resources: For cost tracking and organization
- Regular Updates: Keep OS and packages updated
- Monitoring: Enable Azure Monitor for VMs
- 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.