Managing Proxmox VMs and LXCs with Terraform

There are a few Proxmox provider implementations available on the Terraform registry, this post specifically uses this one: https://registry.terraform.io/providers/loeken/proxmox/latest/docs Other implementations might behave slightly differently but I haven't tried. This particular implementation has a weird behavior: it tries to recreate the disk for a virtual machine when running terraform apply - which then fails on the server's side. Fortunately it doesn't stop me from creating more resources e.g. when I add a new LXC to the file, it will still create the LXC just fine, so it kinda "works". Compared to other services that support IaC (e.g. an AWS EC2 instance), I'm not sure if it's this particular implementation, or if it's the API on Proxmox's side, the whole experience does not feel smooth. It wants to do some "updates" when I haven't changed a single letter, which then doesn't actually seem to change anything, again it mostly "works", and it's better than manually clicking through a few steps each time I want to create a new VM/LXC through the web UI, so it's good enough to me. My Terraform file looks like this, hopefully it's helpful to you if you are trying to do something similar: terraform { required_providers { proxmox = { source = "loeken/proxmox" version = ">=2.9.0" } } required_version = ">= 0.14" } provider "proxmox" { # Default HTTPS port of Proxmox is 8006, yours might be different pm_api_url = "https://xxxx:8006/api2/json" # I thought this was required as my Proxmox server uses a self-signed certificate. But apparently it works without this set to true anyway #pm_tls_insecure = true # Obviously not the best practice, you should use environment variables PM_USER and PM_PASS instead. I only do this because I am in a home lab environment pm_user = "root@pam" pm_password = "secureultrapluspromax" } # https://registry.terraform.io/providers/loeken/proxmox/latest/docs/resources/vm_qemu resource "proxmox_vm_qemu" "resource_name_here" { name = "VM name here" target_node = "Name of the node (under the Datacenter node)" vmid = 200 cores = 16 memory = 65536 # MiB os_type = "ubuntu" # The volume name under the target node "local", then the storage type "ISO Images", then the image's name iso = "local:iso/ubuntu-24.10-live-server-amd64.iso" disk { type = "scsi" size = "50G" storage = "local-lvm" } network { bridge = "vmbr0" firewall = false link_down = false model = "e1000" } } # https://registry.terraform.io/providers/loeken/proxmox/latest/docs/resources/lxc resource "proxmox_lxc" "resource_name_here" { target_node = "Name of the node (under the Datacenter node)" hostname = "hostname" # The volume name under the target node "local", then the storage type "CT Templates", then the template's name ostemplate = "local:vztmpl/ubuntu-22.04-standard_22.04-1_amd64.tar.zst" # Root password password = "secureultrapluspromax" unprivileged = true vmid = 201 cores = 4 memory = 4096 # MiB // Terraform will crash without rootfs defined rootfs { storage = "local-lvm" size = "2G" } features { mount = "nfs" } # Comments from the provider's doc, I keep it here to remind myself of this weird bug # // NFS share mounted on host # // Without 'volume' defined, Proxmox will try to create a volume with # // the value of 'storage' + : + 'size' (without the trailing G) - e.g. # // "/srv/host/bind-mount-point:256". # // This behaviour looks to be caused by a bug in the provider. mountpoint { key = "0" slot = 0 storage = "/mnt/mountpoint" volume = "/mnt/mountpoint" mp = "/mnt/data" # Unintuitively (and if I remember correctly), this does not work without specifying a size size = "1T" } network { name = "eth0" bridge = "vmbr0" ip = "dhcp" } }

Jan 17, 2025 - 03:23
Managing Proxmox VMs and LXCs with Terraform

There are a few Proxmox provider implementations available on the Terraform registry, this post specifically uses this one: https://registry.terraform.io/providers/loeken/proxmox/latest/docs

Other implementations might behave slightly differently but I haven't tried. This particular implementation has a weird behavior: it tries to recreate the disk for a virtual machine when running terraform apply - which then fails on the server's side. Fortunately it doesn't stop me from creating more resources e.g. when I add a new LXC to the file, it will still create the LXC just fine, so it kinda "works".

Compared to other services that support IaC (e.g. an AWS EC2 instance), I'm not sure if it's this particular implementation, or if it's the API on Proxmox's side, the whole experience does not feel smooth. It wants to do some "updates" when I haven't changed a single letter, which then doesn't actually seem to change anything, again it mostly "works", and it's better than manually clicking through a few steps each time I want to create a new VM/LXC through the web UI, so it's good enough to me.

My Terraform file looks like this, hopefully it's helpful to you if you are trying to do something similar:

terraform {
  required_providers {
    proxmox = {
      source  = "loeken/proxmox"
      version = ">=2.9.0"
    }
  }
  required_version = ">= 0.14"
}

provider "proxmox" {
  # Default HTTPS port of Proxmox is 8006, yours might be different
  pm_api_url = "https://xxxx:8006/api2/json"
  # I thought this was required as my Proxmox server uses a self-signed certificate. But apparently it works without this set to true anyway
  #pm_tls_insecure = true
  # Obviously not the best practice, you should use environment variables PM_USER and PM_PASS instead. I only do this because I am in a home lab environment
  pm_user     = "root@pam"
  pm_password = "secureultrapluspromax"
}

# https://registry.terraform.io/providers/loeken/proxmox/latest/docs/resources/vm_qemu
resource "proxmox_vm_qemu" "resource_name_here" {
  name        = "VM name here"
  target_node = "Name of the node (under the Datacenter node)"
  vmid        = 200
  cores       = 16
  memory      = 65536 # MiB
  os_type     = "ubuntu"
  # The volume name under the target node "local", then the storage type "ISO Images", then the image's name
  iso         = "local:iso/ubuntu-24.10-live-server-amd64.iso"

  disk {
    type    = "scsi"
    size    = "50G"
    storage = "local-lvm"
  }

  network {
    bridge    = "vmbr0"
    firewall  = false
    link_down = false
    model     = "e1000"
  }
}

# https://registry.terraform.io/providers/loeken/proxmox/latest/docs/resources/lxc
resource "proxmox_lxc" "resource_name_here" {
  target_node = "Name of the node (under the Datacenter node)"
  hostname     = "hostname"
  # The volume name under the target node "local", then the storage type "CT Templates", then the template's name
  ostemplate   = "local:vztmpl/ubuntu-22.04-standard_22.04-1_amd64.tar.zst"
  # Root password
  password     = "secureultrapluspromax"
  unprivileged = true
  vmid         = 201
  cores        = 4
  memory       = 4096 # MiB

  // Terraform will crash without rootfs defined
  rootfs {
    storage = "local-lvm"
    size    = "2G"
  }

  features {
    mount = "nfs"
  }

  # Comments from the provider's doc, I keep it here to remind myself of this weird bug
  # // NFS share mounted on host
  # // Without 'volume' defined, Proxmox will try to create a volume with
  # // the value of 'storage' + : + 'size' (without the trailing G) - e.g.
  # // "/srv/host/bind-mount-point:256".
  # // This behaviour looks to be caused by a bug in the provider.
  mountpoint {
    key     = "0"
    slot    = 0
    storage = "/mnt/mountpoint"
    volume  = "/mnt/mountpoint"
    mp      = "/mnt/data"
    # Unintuitively (and if I remember correctly), this does not work without specifying a size
    size    = "1T"
  }

  network {
    name   = "eth0"
    bridge = "vmbr0"
    ip     = "dhcp"
  }
}