proxmox-lab
Nine Proxmox VMs managed with Terraform and remote state in Azure Blob Storage. This project covers provisioning new machines, importing existing infrastructure and handling resource changes safely.
- Terraform
- Proxmox
- Azure
- IaC
Nine VMs on one Proxmox box, defined in Terraform with the bpg/proxmox provider.
State lives in Azure Blob Storage.
Why I moved to Terraform
Half the lab was built by hand. That was fine right up until I wanted to change something and couldn’t remember what I’d set six weeks earlier.
Working with an existing lab introduced an important constraint: some VMs could be recreated, while others contained configuration or data I needed to preserve. I had to account for that before bringing them under Terraform management.
Provisioning new VMs and importing existing ones
The Kairos and Ubuntu lab machines are provisioned by Terraform. It creates them, it can destroy them, and I don’t mind if it does.
My control station and the Talos nodes existed before any of this and were imported into state. Those carry:
lifecycle {
prevent_destroy = true
}Terraform now refuses to plan a destroy on them, rather than offering me the chance to approve one by accident. It only covers changes made through Terraform while that block is in the configuration: removing the resource block removes the protection with it, and nothing stops someone deleting the VM from the Proxmox web UI.
Current structure and next steps
The configuration currently uses one resource block per VM. This makes each machine easy to inspect, but introduces repetition. A next step is to identify which settings belong in a reusable module.
State in Azure
State goes to proxmox/terraform.tfstate. Auth is Entra ID through az login, so
there’s no storage account key sitting in the Terraform config.
The Proxmox API token is loaded from a gitignored file, and saved plans are
gitignored too. A tfplan holds resource values in the clear and is not something
to push by accident.
source .proxmox-env
terraform plan -out tfplan
terraform apply tfplanI read the plan before applying it. Properly, line by line, when there’s an import or a rename in it. Which brings me to the useful mistake.
The rename that wanted to delete a VM
I renamed a resource in the config. Terraform read that as: destroy cp_01, create
kairos_cp. Same machine, new address, and nothing in the world tells Terraform
those are the same thing unless I say so:
moved {
from = proxmox_virtual_environment_vm.cp_01
to = proxmox_virtual_environment_vm.kairos_cp
}This showed me why Terraform resource addresses matter, and how a moved block can
preserve the relationship between an existing VM and its configuration.
Current limitations
The Kairos ISO comes from a local build path, and old images pile up until I delete them by hand.