How to Set Up Proxmox with Terraform: A Step-by-Step Guide

Sumit SurSumit Sur
4 min read

Whether you’re just dipping your toes into Terraform or you’ve been automating your infrastructure for years, Hooking up Terraform with Proxmox is a simple step that unlocks a lot of automation power.

By the end of this guide, you’ll have learned how to:

  1. Create a dedicated Proxmox role for Terraform

  2. A service user and API token

  3. A reusable Cloud‑Init template and snippets for quick VM creation

đź‘‹ Quick heads-up!

This guide is Part 1 of a multi-part series.

In this article, we’ll walk through setting up Proxmox for use with Terraform—creating roles, users, API tokens, and a Cloud-Init template.

In Part 2, we’ll use Terraform to deploy a real VM from this setup—complete with configuration and automation.

👉 Jump to Part 2: Deploy Your First VM →

Create a “Terraform-Friendly” Role

Let’s give Terraform only the permissions it truly needs: allocating disks, cloning VMs, tweaking cloud‑init settings, and so on. This is called privilege separation—and it’s a best practice to keep your environment secure.

Log into the Proxmox cluster or host using ssh

  • Create a new role Terraform_Provisioner_role

  • Create the user terraform-user@pve

  • Add the Terraform_Provisioner_role role to terraform-user

#create the role
pveum role add Terraform_Provisioner_role -privs "Datastore.AllocateSpace Datastore.AllocateTemplate Datastore.Audit Pool.Allocate Sys.Audit Sys.Console Sys.Modify VM.Allocate VM.Audit VM.Clone VM.Config.CDROM VM.Config.Cloudinit VM.Config.CPU VM.Config.Disk VM.Config.HWType VM.Config.Memory VM.Config.Network VM.Config.Options VM.Migrate VM.Monitor VM.PowerMgmt SDN.Use"
#create the user
pveum user add terraform-user@pve --password <password>
#assign the role to the user
pveum aclmod / -user terraform-user@pve -role Terraform_Provisioner_role

Generate an API Token

Rather than embedding a password in plain text, we’ll use an API token. This is both safer and more portable—especially in CI/CD pipelines.

You’ll see your token’s ID and secret in the output—jot them down for the next step.

pveum user token add terraform-user@pve tf_token

Output:

If you’re running Terraform from GitHub Actions, GitLab CI, or another platform, stash these in your project’s secret variables.

Disable Privilege separation

In our Terraform use case, we usually assign permissions to the user account (like terraform-user@pve), not directly to the token itself. So if "Privilege Separation" is enabled, the token might not have access to all the necessary resources—even though the user does!

To make sure the token works as expected, Privilege Separation must be disabled, so the token inherits the user's full role permissions.

Connecting from Terraform to Proxmox

We are using the Telmate Proxmox Terraform provider

Terraform needs two environment variables: the token ID (which includes your username) and the token secret. Here’s how you’d set them in your shell:

When using the Telmate Proxmox Terraform provider, you authenticate using environment variables that start with PM_. These are used by Terraform to connect to the Proxmox API.

# use single quotes for the API token ID because of the exclamation mark
export PM_API_TOKEN_ID='terraform-user@pve!tf_token'
export PM_API_TOKEN_SECRET="XXXXXX-XXXX-XXXXX-XXXX-XXXXXXXXXXX"

If using git actions, you can store these in repository variables

Creating a Cloud Init Template

Terraform works best when you have a VM template ready. We’ll start with Ubuntu 24.04 LTS cloud image

  • This will download the image under /var/lib/vz/template/iso in the proxmox server

  • Install guest tools

    • login to the proxmox server over ssh
# as root on your Proxmox host

apt update
apt install -y libguestfs-tools
  • Using the guest tools , we will customize the image to install:

    • qemu-guest-agent

    • Clear the existing machine ID in the image by setting the files to zero size. This ensures that each cloned VM will generate a new machine ID

sudo virt-customize -a ubuntu_24-04-server-cloudimg-amd64_copy.img \
  --install qemu-guest-agent \
  --run-command 'systemctl start qemu-guest-agent'

sudo virt-customize -a ubuntu_24-04-server-cloudimg-amd64_copy.img \
  --run-command "truncate -s 0 /etc/machine-id /var/lib/dbus/machine-id"

Create the VM for Template

#create the VM shell
qm create 9000 --name ubuntu-24-04-cloudinit
#import the cloid image to the vm disk
qm set 9000 --scsi0 local-lvm:0,import-from=/var/lib/vz/template/iso/noble-server-cloudimg-amd64.img

Convert to template

#convert to template
qm template 9000

Template created

Creating a Snippet

Snippets let you inject arbitrary cloud‑init YAML at VM creation time. Here’s how to set up a folder and add two quick examples:

mkdir /var/lib/vz/snippets

Now that we have a place to store the snippet, we can create the snippet itself. The following command will create a snippet install-packages.yml :

tee /var/lib/vz/snippets/install-packages.yml <<EOF
#cloud-config
runcmd:
  - apt update
  - apt install -y curl jq
EOF

Wrapping Up

And that’s it! You now have:

  • A Terraform role limited to exactly what you need

  • A service user and token for secure API access

  • A cloud‑init template ready to spin up Ubuntu VMs

  • Handy snippets for customization


To learn how to use the template you just built to create real, working virtual machines with cloud-init, snippets, and more.


0
Subscribe to my newsletter

Read articles from Sumit Sur directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Sumit Sur
Sumit Sur