Orchestrating with Terraform
In my previous post we used Proxmox:
- Created a Linux VM
- Customized the Linux VM
- Created a Template of the Linux VM
- Used Terraform to deploy the Linux VM
That’s great, but what’s the point of using Terraform to deploy a single VM?
Well, the purpose is to create templates to reduce the workload; to create something that is easily repeatable with a single command line. But true orchestration would be to be able to replicate that for an entire environment.
Let’s start with deploying multiple VMs from a single template. The template is just the base line – it’s assigned 2 vCPU cores, and 2 GB of memory.
First – Check Resources. When you type in ‘yes’ to apply your plan, are you going to break your server? Do you have enough resource to deploy 5 or 10 or 20 VMs or Containers?
Assuming you do have the resources, let’s revisit our single deployment script:
File: rockylinux.tf
resource “proxmox_virtual_environment_vm” “rocky-server” {
name = “terraform-rocky”
node_name = “proxmox4”clone {
vm_id = 9000
}cpu {
cores = 2
type = “x86-64-v2-AES”
}memory {
dedicated = 2048
}disk {
datastore_id = “local-lvm”
size = 20
interface = “scsi0”
}network_device {
bridge = “vmbr0”
}initialization {
ip_config {
ipv4 {
address = “dhcp”
}
}
user_account {
username = var.vm_admin
password = var.vm_password
keys = [var.ssh_public_key]
}
dns {
servers = var.dns_servers
domain = var.domain
}
}provisioner “local-exec” {
command = “sleep 120” # Wait 2 minutes for cloud-init
}
}
Now we’ll add another variables.tf
variable “vms” {
type = map(object({
cores = number
memory = number
disk = number
template_id = number
}
)
)default = {
web1 = {
cores = 2
memory = 4096
disk = 40
template_id = 9000
}
web2 = {
cores = 2
memory = 4096
disk = 40
template_id = 9000
}
db1 = {
cores = 4
memory = 16384
disk = 100
template_id = 9000
}
app1 = {
cores = 8
memory = 32768
disk = 80
template_id = 9001
}
}
}
We’re setting up different conditions and resource configurations for each server we’re deploying based on function. A web server with 2 vCPU cores and 4 GG memory, or a db server with 4 cores and 16 GB memory. Same template, so our cloud-init is the same for each server ensuring we can log in, the qemu agent is running, etc., but now each VM deployed has a different name designating it’s purpose.
Update the rockylinux.tf (or create a new file)
resource “proxmox_virtual_environment_vm” “servers” {
for_each = var.vms
name = each.key # Uses the map key as VM name
node_name = “proxmox4”
clone {
vm_id = each.value.template_id
}
cpu { cores = each.value.cores
type = “x86-64-v2-AES”
}
memory {
dedicated = each.value.memory
}
disk {
datastore_id = “pvs-zfs”
size = each.value.disk interface = “scsi0”
}
network_device {
bridge = “vmbr0”
}initialization {
ip_config {
ipv4 {
address = “dhcp”
}
}
user_account {
username = var.vm_admin
password = var.vm_password
keys = [var.ssh_public_key]
}
dns {
servers = var.dns_servers
domain = var.domain
}
}provisioner “local-exec” {
command = “sleep 120” # Wait 2 minutes for cloud-init
}
}
resource "proxmox_virtual_environment_vm" "servers" {
for_each = var.vms
name = each.key # Uses the map key as VM name
node_name = "proxmox4"
clone {
vm_id = each.value.template_id
}
cpu {
cores = each.value.cores
type = "x86-64-v2-AES"
}
memory {
dedicated = each.value.memory
}
disk {
datastore_id = "pvs-zfs"
size = each.value.disk interface = "scsi0"
}
network_device {
bridge = "vmbr0"
}
initialization {
ip_config {
ipv4 {
address = "dhcp"
}
}
user_account {
username = var.vm_admin
password = var.vm_password
keys = [var.ssh_public_key]
}
dns {
servers = var.dns_servers
domain = var.domain
}
}
provisioner “local-exec” {
command = "sleep 120" # Wait 2 minutes for cloud-init
}
}



