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

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**](https://automatestack.dev/how-to-deploy-your-first-proxmox-virtual-machine-using-terraform) [**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`
    

```bash
#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.

```bash
pveum user token add terraform-user@pve tf_token
```

Output:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1749611065706/7301e4df-7836-478b-9e34-51a4ee0f55e6.png align="left")

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.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1749653696103/c53f386f-7d48-4b8d-bc5e-d6bb8959f807.png align="center")

## **Connecting from Terraform to Proxmox**

We are using the [Telmate Proxmox Terraform provider](https://registry.terraform.io/providers/Telmate/proxmox/latest/docs)

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.

```bash
# 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

* Download the Ubuntu cloud image. I choose the [ubuntu server 24.04 LTS cloud image](https://cloud-images.ubuntu.com/noble/current/)
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1749616743956/fd062e42-ec6b-4573-a291-b2ca7bb12a72.png align="left")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1749616799755/958ce57a-4db2-4fd0-a91e-a5b3cde8baea.png align="left")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1749616841923/ac2d84a4-141a-4982-a015-694f5345d56d.png align="left")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1749617016960/6900f7c2-2ae3-4f44-a824-c3724bc3540f.png align="left")

* 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
        

```bash
# 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
        

```bash
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

```bash
#create the VM shell
qm create 9000 --name ubuntu-24-04-cloudinit
```

```bash
#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
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1749617702684/7f4cb807-d992-4b67-8f7d-f0f6b71ba532.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1749617725107/6301aabe-2cc1-418f-9d20-da89b7acdf65.png align="center")

### Convert to template

```bash
#convert to template
qm template 9000
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1749617765011/4375d35e-de91-4374-bbe4-af911ab6e8e2.png align="center")

### Template created

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1749618000713/29beb744-c47f-4ab6-88d5-eaf724e133e1.png align="center")

## **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:

```bash
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` :

```bash
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.

## 👉 [**🚀 Read Next: Deploy Your First Proxmox VM with Terraform**](https://automatestack.dev/how-to-deploy-your-first-proxmox-virtual-machine-using-terraform)

---
