Skip to content

Terraform

Terraform provisions VMs on Proxmox by cloning Packer-built templates.

Quick Start

# Deploy all VMs
task terraform:deploy ENV=wil

# Deploy a single VM
task terraform:deploy-vm ENV=wil VM=networking

# Destroy a single VM
task terraform:destroy-vm ENV=wil VM=networking

# Clean up state files
task terraform:clean ENV=wil

How It Works

VMs are defined as a map in terraform/environments/<env>/vms.auto.tfvars. The root module iterates over this map with for_each and calls the VM module for each entry.

terraform/
├── main.tf                          # Root module (for_each over vms)
├── modules/vm/
│   ├── main.tf                      # proxmox_vm_qemu resource
│   └── variables.tf                 # VM variable definitions
└── environments/
    ├── wil/
    │   ├── main.tf                  # Calls root module
    │   ├── variables.tf             # Variable declarations
    │   ├── providers.tf             # Proxmox provider config
    │   ├── vms.auto.tfvars          # VM definitions
    │   └── terraform.tfstate        # State file
    └── ldn/

VM Definition Format

Each VM is an entry in the vms map in vms.auto.tfvars:

vms = {
  networking = {
    name           = "networking"
    description    = "Networking VM"
    proxmox_node   = "proxmox"
    vmid           = 1000
    template_name  = "ubuntu-server-wil-base"
    ip_address     = "10.2.20.53"
    gateway        = "10.2.20.1"
    nameserver     = "10.2.20.53"
    cores          = 2
    memory         = 4096
    disk_size      = "50G"
    storage_pool   = "local-lvm"
    network_bridge = "vmbr0"
    tags           = "infrastructure"
    ssh_user       = "sfcal"
  }

  ca_server = {
    name           = "ca"
    description    = "Certificate Authority"
    proxmox_node   = "proxmox"
    vmid           = 1003
    template_name  = "ubuntu-server-wil-base"
    ip_address     = "10.2.20.9"
    gateway        = "10.2.20.1"
    nameserver     = "10.2.20.53"
    cores          = 1
    memory         = 4096
    disk_size      = "20G"
    storage_pool   = "local-lvm"
    network_bridge = "vmbr0"
    tags           = "infrastructure"
    ssh_user       = "sfcal"
  }
}

VM Module Variables

Variable Type Default Description
name string required VM hostname
description string "Virtual Machine" Proxmox description
proxmox_node string required Target Proxmox host
vmid number required Unique VM ID
template_name string required Packer template to clone
tags string "" "infrastructure" or "application"
onboot bool true Auto-start on Proxmox boot
cores number 2 CPU cores
memory number 2048 RAM in MB
disk_size string "20G" Root disk size
storage_pool string required Proxmox storage pool
network_bridge string "vmbr0" Network bridge
ip_address string required Static IP (assumes /24)
gateway string required Default gateway
nameserver string required DNS server
ssh_user string required SSH username
ssh_public_key string required Passed from environment variables

WIL Environment VMs

Key Name VMID IP Cores Memory Disk Tags
networking networking 1000 10.2.20.53 2 4GB 50G infrastructure
ca_server ca 1003 10.2.20.9 1 4GB 20G infrastructure
ntp_server ntp 1002 10.2.20.123 1 1GB 20G infrastructure
monitoring_server monitoring 1107 10.2.20.30 1 8GB 50G infrastructure
web_server web 1109 10.2.20.45 2 4GB 20G application
games_server games 1111 10.2.20.50 1 8GB 50G application
work_server work 1112 10.2.20.60 4 8GB 256G application
seafile_server seafile 1113 10.2.20.70 2 8GB 50G application

Implementation Details

  • Full clones — VMs are full clones, not linked. Independent of templates after creation.
  • Cloud-init — Static IP configured via ipconfig0 = "ip=<address>/24,gw=<gateway>". Assumes /24 subnets.
  • State — Per-environment state files at terraform/environments/<env>/terraform.tfstate.
  • QEMU agent — Enabled on all VMs (installed in Packer templates).

Troubleshooting

"template not found" — The template_name must match a Packer-built template. Check with qm list on the Proxmox host. Names follow ubuntu-server-<env>-base.

"VMID already exists" — Another VM uses that ID. Check qm list or the existing entries in vms.auto.tfvars.

State out of sync — If a VM was deleted outside Terraform, clean state with task terraform:clean ENV=wil and redeploy.

Provider authentication error — Verify Proxmox API credentials in the encrypted terraform.tfvars file. Decrypt with sops terraform/environments/<env>/terraform.tfvars.