Overview
This project takes a look into using the Infrastructure as Code (IaC) tool Terraform for managing a Proxmox environment and automating deployments for VMs and container workloads. The topics in this article include the initial setup using a Service Account, initial configuration, and an example Terraform deployment of two Proxmox VMs.
What is Terraform?
Terraform is a free to use Infrastructure as Code (IaC) tool designed by HashiCorp that utilizes a declarative language along with a state file to manage infrastructure deployments. The state file is used for maintaining a persistent record of the infrastructure deployed by Terraform.
What is Infrastructure as Code?
Infrastructure as Code (IaC) is the concept of defining an environment or system using code. This can be a programming language such as Python, or a declarative manifest language such YAML or HCL. Using IaC helps to maintain a repeatable and consistent deployment each time it is run.
IaC solves the issue where using “ClickOps” (managing environments via web portal or GUI) can be time-consuming for large scale deployments, or sometimes produce different results due to accidental human error or changes an interface. Using IaC can be much faster when deploying a large number of resources simultaneously.
Types of IaC
Imperative:
- Iterates through specific steps to provision each resource.
- Similar to following each line in a script file.
- Languages: Powershell, Bash, Python, Azure CLI.
Declarative:
- Defines the desired state of the environment.
- Uses manifest files to define how the environment should be.
- Languages: HCL (Terraform), YAML (Ansible, Docker, Kubernetes), Bicep (Azure).
What is a Service Account, and why should I use one?
A Service Account is capable of authentication to a system, but dedicated to a specific use only. Unlike a regular user or administrator account, a Service Account is not typically used for local or web GUI login access, but instead authenticates using automated methods (SAML, API etc). In the context of this project, the Service Account is dedicated for Terraform deployments and will only be used for that singular purpose. This ensures that any action made by this account can be identified as part of the Terraform workflow.
Requirements
- An existing, working Proxmox environment.
- Network connectivity to Proxmox from local device or management VM.
- Install Terraform (see here for information).
- VM Template (details provided in a below section).
Note: The commands provided in this guide are targeted for Linux systems, however any local commands that may be required can be used with Windows Subsystem for Linux (WSL).
Create Service Account for Terraform (API Token)
From within the Proxmox web portal, navigate to Datacenter > Permissions > Users. Click the Add button to create a new user account, entering the desired username and password.
Make sure to change the Realm dropdown item to Proxmox VE authentication server. This ensures the account is contained with Proxmox only, and not related to the host operating system. Once done, click Add.

Now we need to add the required permissions to the new account.
Navigate to Datacenter > Permissions and click the Add button.
Select the path /, the newly created user account, and the role Administrator.
Ensure that the tick box Propagate is enabled to deploy the permissions.
Click Add to complete the permissions assignment.

Although it is possible to authenticate Terraform with a username and password, it is considered more secure to utilize the API token method and simply rotate keys as required or on a regular basis.
Navigate to Datacenter > Permissions > API Tokens and click the Add button.
Select the Terraform service account from the User dropdown menu.
Enter a name for the token assignment (not important, can be anything).
Ensure that the option Privilege Separation is un-ticked as this will split API permissions from the already assigned user permissions.
- Optional: Set an expiry date for the API token to ensure that long lived keys are not used. Utilize a calendar/reminder or other means for managing this.
Click Add to complete the API token assignment.
Once created, the token secret will be shown only once!
Ensure that you store this securely in a password manager, Azure Key Vault or other secure location.
This will be needed later in the Terraform configuration.


We can now test this API token using the Curl command from the terminal.
Executing the following command should result in a response.
Make sure to replace the following with your own token and host address values.
|
|

Prepare VM Template
Install Required Tools
From within the Proxmox web interface, navigate to one of the Proxmox hosts and access the terminal (shell).
Install the package libguestfs-tools. This will provide functionality to inject the image with the Qemu guest agent. The Qemu guest agent is used to exchange information between the host and guest, and to execute commands in the guest OS.
|
|

Cloud-Init Image File Preparation
Using a Ubuntu cloud-init image speeds up and standardizes VM deployment by providing initial parameters such as hostname, IP address and SSH keys. This method of using a single template will allow us to spin up multiple VMs in the same deployment, each with different settings.
Download the image file from the URL provided. If another version is required, the URL can be browsed by removing the section after https://cloud-images.ubuntu.com/.
|
|
Once the image file has been downloaded, we can resize/expand the image. This will allow for more useable disk space later on.
|
|
Use the ‘virt-customize’ tool to inject the image file with the Qemu Guest Agent package, a helper daemon, which is installed in the guest. It is used to exchange information between the host and guest (VM).
Note: It is possible to install other packages that may be wanted or need in the base template at this point.
|
|
If required, the root password can be set at this point also, using the command below.
|
|

Provision Initial VM & Convert to Template
With the image now ready for use, we can proceed to create a VM that will later be converted into a template. From a Proxmox host terminal session, execute the following command to create a new VM. The VM should appear in the VM list on the left hand side menu of the Proxmox web interface.
|
|

Convert the new VM into a template so we can use it with Terraform for automating deployments. The icon representing the VM will change from the VM icon, to the template icon when completed.
|
|

Terraform Configuration
Create & Populate Terraform Files
Terraform uses configuration files (.tf) to define resources and setup Terraform components. The next step is to create a working directory for the project. Run the following commands to create a new directory containing the required files.
|
|
- providers.tf
- Defines and configures the providers required by a Terraform configuration.
- A provider is a plugin that allows Terraform to interact with an external service or API (Proxmox, Azure etc).
- main.tf
- Used to define the core/main resources for a deployment (VM, networks, containers etc).
- variables.tf
- Used to define variables that are required and referenced within the main configuration.
- proxmox.tfvars
- Contains variable inputs and secret values that should not be in the main code files.
Populate the new files as per below, replacing the example values with your own.
providers.tf
|
|
variables.tf
|
|
proxmox.tfvars
|
|
main.tf
|
|
Execute Terraform Deployment
With the Terraform files now populated, we can execute the Terraform commands from our local machine to deploy the resources (VMs).
Initialize
Execute the following to prepare Terraform in the current working directory.
This process will download the providers defined in the providers.tf file and create a Terraform configuration directory, along with files related to the state.
|
|


Plan & Apply
Next, we can review the intended changes by running a Terraform plan. During this process, Terraform will compare the file contents with it’s current state file and produce an output of what changes it intends to make on the target system (Proxmox in this case).
Note: By default, Terraform is not aware of existing resources created outside of using Terraform. If a VM were to be created in the Proxmox web interface, Terraform is not aware of it. Terraform does provide a method of importing existing resources. Documentation on that process found here.
|
|
The output on screen shows the additions indicated by the + symbol. This shows that two new resources (the VMs) will be created with the shown configurations. The plan file has been saved locally. We can use this with the apply command to ensure that the deployment uses this exact plan.
Symbol Guide:
- Add:
+ - Modify:
~ - Remove:
-

With the plan reviewed and approved, we can now proceed with the deployment. Run the apply command, followed with confirmation by entering y when prompted. To avoid this prompt, use the --auto-approve command switch. Pass in the variable values file (TFVARS) and the plan file from the previous output.
|
|

The new VMs should now be visible in the Proxmox web interface. Running the same command again will display a message that the current environment already matches the configuration.
|
|

Destroy
This final step provides a way to remove all resources deployed. As with the apply command, you will be prompted to enter y unless using the --auto-approve command switch.
|
|
Once the process completes, the resources will no longer be listed in the Proxmox GUI, and the events related to their removal will be recorded.


Running the plan and apply commands again should produce the same results every time. This is one of the benefits of using Infrastructure as Code. It helps to produce repeatable and reliable deployments each time it is executed. Using IaC with automation tools like Github Actions, Gitlab or Azure DevOps can help to ensure consistency within an environment and automate deployment tasks using schedules or event triggers, such as git commits or pull requests.
Cover photo by Mourizal Zativa on Unsplash