Terraform + Proxmox = <3
<--
I am tired of UIs
Up until now, every time i wanted to deploy a VM in my homelab i did it with the Proxmox GUI. Don’t get me wrong, the GUI is nice, but i would like to not have to repeat the same mundane task every time i want a new VM.
There is an argument to be made that i probably shouldn’t run that many VMs, and that for the number of VMs i need, this isn’t worth it. However, being able to deploy a VM by just changing a few variables in a file and running terraform apply
just tickles something in my nerd brain.
I also really like the repeatability of this, as i use these VM definitions to deploy K3S hosts, docker hosts and others where i want a “default” setup.
Initial requirements
To use Terraform with Proxmox we use a privoder created by Telmate. We create the initial provider.tf
file as so:
terraform {
required_providers {
proxmox = {
source = "telmate/proxmox"
version = "3.0.1-rc3"
}
}
}
variable "proxmox_api_url" {
type = string
}
variable "proxmox_user" {
type = string
sensitive = true
}
variable "proxmox_password" {
type = string
sensitive = true
}
variable "ssh_public_key" {
type = string
}
provider "proxmox"{
pm_api_url = var.proxmox_api_url
pm_user = var.proxmox_user
pm_password = var.proxmox_password
pm_tls_insecure = true
pm_otp = ""
}
I have stored all secrets to access the proxmox API in custom variables located in a .auto.tfvars
file that i do not track in git. Those variables are defined in the provider such that if they are not present Terraform will complain.
In this step i had some trouble, as you can see i use a release candidate version. There seems to be a bug in version 2.9.3
, and instead of trying to track it down i just switched to release candidate.
Defining the virtual machine
Now as i mentioned previously, i already have templates created in my proxmox cluster (i made these with Ansible). Therefore i can use these as a base for the provisioning of a new VM.
Here is an example definition of a VM
resource "proxmox_vm_qemu" "havneboks" {
name = "havneboks"
desc = "Docker master"
target_node = "poseidon"
agent = 1
onboot = true
clone = "VM 9001"
cores = 4
sockets = 1
cpu = "host"
memory = 3096
# Setup the disk
disks {
ide {
ide2 {
cloudinit {
storage = "basseng"
}
}
}
scsi {
scsi0 {
disk {
size = "10G"
storage = "basseng"
}
}
}
}
network {
bridge = "vmbr0"
model = "virtio"
}
scsihw = "virtio-scsi-pci"
os_type = "cloud-init"
ipconfig0 = "ip=192.168.1.51/24,gw=192.168.1.1"
nameserver = "192.168.1.69"
ciuser = "ansible"
sshkeys = var.ssh_public_key
}
A VM is defined using the resource type of proxmox_vm_qemu
with a name. I really like to use the names of the service just translated to norwegian, so in this case, this VM is called havneboks
(meaning docker box).
- name: The name of the VM (hostname)
- desc: A description of the VM
- target_node: Which node in the cluster should the VM be provisioned to, in this case i provision it to the node
poseidon
(hostname) - agent: Just select 1
- onboot: Set the VM to start when the host boots
- clone: Which template to clone the VM from
- cores: How much horsepowa u want?
- sockets: I only got 1 cpu in each of my boxes
- memory: How much RAM u want?
Now, a really important part of this is the disk setup, as you have to mimic the setup of the template (sizes can be chosen freely). So in my case, i have the cloud-init disk on ide2
and the OS disk on scsi0
in the template. Therefore we create the same exact setup for this resource.
- network: Just mimic the cloud-init
- scsihw: Which hardware do you want the host system to use to provide scsi?
- os_type: I use cloud-init, so we select cloud-init
- ipconfig: Now this is quite interesting, you should set a static IP and gateway such that the VM starts with a proper IP adress (you can also use DHCP here)
- namesever: I use a custom dns on adress
.69
(nice) so i set that, but if this is not set it will use “same as host” - ciuser: A user to be created by cloud-init for this VM
- sshkeys: Initial SSH public keys to allow access. This is stored in a variable in my case.
Just run?
Bing bang bom. You can now deploy your VM fully automatically and about 43 seconds later access it via SSH. Now all i did was configure it with ansible, and i have a fully reproducible homelab setup.
If you want to have a look at my homelab infrastructure as code repo, you can find it at polsevev/homelab