Automation is key to maintaining efficient and scalable systems in today’s infrastructure landscape. This guide will walk you through automating VM deployment on Proxmox using Terraform, enabling you to manage your infrastructure as code.
Proxmox Virtual Environment (Proxmox VE) is a powerful open-source virtualization platform. When combined with Terraform, we can automate the creation and management of virtual machines, making our infrastructure reproducible and version-controlled.
Requirements
- A cloud-init-enabled template in Proxmox
- Proxmox VE server installed and running
- Docker
If you haven’t installed these tools yet, don’t worry! Check out the links in the video description for my tutorials on installing them on Windows, Mac, and Ubuntu.
Project Repository
https://github.com/thiagousa/youtube.git
Code Breakdown with Annotations
1. Makefile Annotations
# Directory where Terraform files are located
TERRAFORM_DIR := terraform
# This variable sets the directory containing all Terraform configurations
# Docker image for Terraform
TERRAFORM_IMAGE := hashicorp/terraform:latest
# Specifies which Terraform Docker image to use - latest version
# Common Docker run options
DOCKER_RUN := docker run --rm -v $(PWD)/$(TERRAFORM_DIR):/workspace -w /workspace
# Docker run command breakdown:
# --rm : Remove container after execution
# -v : Mount volume
# $(PWD) : Current directory path
# /workspace : Working directory inside container
# -w /workspace : Set working directory
# Make commands
.PHONY: all
all: init plan apply
# Main command that runs the full deployment sequence
.PHONY: init
init:
$(DOCKER_RUN) $(TERRAFORM_IMAGE) init
# Initializes Terraform, downloads providers and modules
.PHONY: plan
plan:
$(DOCKER_RUN) $(TERRAFORM_IMAGE) plan --out "plan"
# Creates an execution plan and saves it to "plan" file
.PHONY: apply
apply:
$(DOCKER_RUN) $(TERRAFORM_IMAGE) apply "plan"
# Applies the saved plan to create/update infrastructure
.PHONY: destroy
destroy:
$(DOCKER_RUN) $(TERRAFORM_IMAGE) destroy -auto-approve
# Destroys all resources created by Terraform
# -auto-approve skips interactive approval
.PHONY: output
output:
$(DOCKER_RUN) $(TERRAFORM_IMAGE) output
# Shows all outputs defined in Terraform configuration
.PHONY: fix
fix:
$(DOCKER_RUN) $(TERRAFORM_IMAGE) fmt
# Formats Terraform files to canonical format
.PHONY: build
build:
mv terraform/terraform.example terraform/terraform.tfvars
# Creates working tfvars file from example template
2. Main Terraform Configuration Annotations
# Main VM resource definition
resource "proxmox_vm_qemu" "cloudinit-test" {
# Transform list of VM configs into a map for for_each
for_each = { for idx, vm in var.vm_configs : vm.name => vm }
# Breakdown of for_each:
# idx: Index of current item (not used here)
# vm: Current VM configuration object
# vm.name => vm: Creates key-value pair with name as key and full config as value
# Basic VM configuration
name = each.value.name # VM name from config
vmid = each.value.vmid # Unique VM ID in Proxmox
desc = each.value.description # VM description
target_node = var.target_node # Proxmox node to deploy on
clone = var.template # Template to clone from
agent = 1 # Enable QEMU guest agent
# VM Hardware configuration
os_type = "cloud-init" # Use cloud-init for initialization
cores = each.value.cores # CPU cores from config
sockets = 1 # Single CPU socket
vcpus = 0 # Let Proxmox manage vCPUs
cpu = "host" # Use host CPU type
memory = each.value.memory # Memory from config
scsihw = "virtio-scsi-pci" # SCSI hardware type
# Disk configuration
disks {
ide {
ide2 {
# Cloud-init disk configuration
cloudinit {
storage = var.local_storage # Storage location for cloud-init
}
}
}
scsi {
scsi0 {
# Main system disk configuration
disk {
size = each.value.disk_size # Disk size from config
cache = "writeback" # Disk cache mode
storage = "local" # Storage location
replicate = true # Enable replication
}
}
}
}
# Network configuration
network {
model = "virtio" # Network adapter model
bridge = var.network_bridge # Network bridge to connect to
}
# Boot and initialization
boot = "order=scsi0" # Boot from SCSI disk
ipconfig0 = "ip=dhcp" # Use DHCP for IP configuration
# Serial console configuration
serial {
id = 0
type = "socket"
}
# Cloud-init user configuration
ciuser = var.user # Cloud-init username
sshkeys = <<EOF # SSH key injection
${var.ssh_key}
EOF
}
3. Variables Configuration Annotations
# VM configurations variable
variable "vm_configs" {
description = "Configuration for multiple VMs"
# Define the structure for VM configurations
type = list(object({
name = string # VM name
vmid = number # Unique VM ID
disk_size = number # Disk size in GB
cores = number # Number of CPU cores
memory = number # Memory in MB
description = string # VM description
}))
}
# Example VM configurations in terraform.auto.tfvars
vm_configs = [
{
name = "prod-vm" # Production VM
vmid = 201 # Unique ID for prod
disk_size = 32 # 32GB disk
cores = 4 # 4 CPU cores
memory = 4048 # 4GB memory
description = "Production VM"
},
{
name = "dev-vm" # Development VM
vmid = 202 # Unique ID for dev
disk_size = 40 # 40GB disk
cores = 2 # 2 CPU cores
memory = 2048 # 2GB memory
description = "Development VM"
}
]
Usage Instructions with Annotations
- Clone Repository
git clone https://github.com/thiagousa/youtube.git
cd youtube
# Clones project and enters directory
- Create Configuration
make build
# Creates terraform.tfvars from example file
# Edit terraform/terraform.tfvars with your configurations
- Initialize Project
make init
# Downloads required providers and modules
# Sets up Terraform working directory
- Plan Deployment
make plan
# Creates and saves execution plan
# Shows what changes will be made
- Apply Configuration
make apply
# Creates/updates infrastructure based on plan
# Shows progress and results
- Verify Deployment
make output
# Shows output values like IP addresses
- Clean Up (Optional)
make destroy
# Removes all created resources
# Use with caution!
Each command is run inside a Docker container, ensuring a consistent execution environment and dependencies.
Next Steps and Best Practices
Version Control
- Keep terraform.tfvars in .gitignore
- Use terraform. example as template
- Document all variables
Resource Management
- Use meaningful VM IDs
- Plan resource allocation carefully
- Monitor resource usage
Security
- Secure SSH keys
- Use strong passwords
- Follow the least privilege principle
Maintenance
- Regular backups
- Update templates
- Monitor logs