# Howto: Automated FreeBSD VM Deployment with ProxLB and Terraform


In today’s fast-paced IT environments, automation is essential for maintaining efficiency and staying competitive. Whether you're managing a small-scale infrastructure or an enterprise-level system, the ability to deploy virtual machines (VMs) quickly, consistently, and with minimal manual intervention can be transformative. This is where tools like ProxLB [and Te](https://github.com/gyptazy/ProxLB)rraform [come into](https://www.terraform.io/) play, offering a powerful solution for automating the deployment and management of VMs.  
  
Why Terraform and not Ansible? I'm aware that Ansible is a great tool - also for such things - but Ansible might be slow in some cases with too many tasks and not writing own custom modules that improve the overall handling. Terraform is often the better choice when it comes just to build up a base infrastructure from scratch where Ansible can take over after this baseline has been set. So, let's have a look at the other tools used here.  
  
ProxLB is a robust load balancer specifically designed for Proxmox environments, enabling seamless resource distribution across multiple nodes in a Proxmox cluster. It also redistributes the VM's disks on shared storages if needed. This ensures high availability and optimal resource utilization. On the other hand, Terraform is an open-source infrastructure-as-code tool that allows you to define, provision, and manage infrastructure using simple, declarative configuration files.  
  
In this guide, we will explore how to leverage ProxLB and Terraform to automate VM deployments. From automatically finding the best node for a new VM placement within the cluster, setting up your environment to writing Terraform scripts and deploying VMs managed by ProxLB, we’ll cover everything you need to know to streamline this process.  
  
The benefits of automating VM deployments with ProxLB and Terraform are significant. First, automation accelerates the deployment process, allowing you to spin up new environments quickly and with consistent configurations, reducing the potential for human error. As your infrastructure grows, managing it becomes increasingly complex; ProxLB simplifies this by efficiently balancing the load across your VMs, making scaling your system easier. Finally, by codifying your infrastructure with Terraform, you achieve consistency and repeatability, ensuring that every deployment adheres to the same standards and practices, which is crucial for maintaining reliability and security in your systems.  
  
**Goal**  
The primary goal of this solution is to build a fully automated way of creating FreeBSD based VMs on a Proxmox cluster which can also be used in other projects like [my BoxyBSD](https://boxybsd.com/) one - of course, this can also be done by any other OS that comes with cloud images and supports cloud-init. By creating new VMs on the Proxmox cluster, the best perfect matching node in the cluster for placing a new VM object should be evaluated. Afterwards, the VM will be placed and created by a given image by using Terraform with the BP[G provider.](https://github.com/bpg/terraform-provider-proxmox) In this case, a counter in the hostname (e.g., [managed-vm30.boxybsd.com](http://managed-vm30.boxybsd.com)) will be dynamically incremented in an automated way. Also the the cluster node for placing the VM will be provided in a dynamic way to Terraform. To make this work, we will use a very simple sh[ell script,](https://brew.bsd.cafe/gyptazy/proxmox-terraform-bpg-freebsd-vm/src/branch/main/create_vm.sh) which will be called and orchestrates all other tools until the new VM object has been created:  
  
\* create\_[vm.sh](http://vm.sh)  
   *Creating an incremented number*Starting ProxLB (with best node option)  
   *Return the best node for a new VM placement*Starting Terraform with dynamic values for hostname and node  
  
All required content and manifests for this howto can be found in my gi[t at](https://brew.bsd.cafe/gyptazy/proxmox-terraform-bpg-freebsd-vm) [brew.bsd.cafe](http://brew.bsd.cafe).  
  
**Requirements**  
This howto assumes that you already have a dedicated Debian based management VM in place which can reach the Proxmox cluster by the API. For further usage we will also require the [*ProxLB*](https://proxlb.de/) and [Terraform](https://www.terraform.io/) with the [BPG provider](https://github.com/bpg/terraform-provider-proxmox). All required steps will be described in detail.  
  
**Installation**  
***General***  
Before installing all the major toolings, we should ensure to have all other things in place. So, we don't need to install additional packages later anymore and can proceed straight-forward with the setup. As a result, we simply install the following packages:  
  
   apt-get install git apt-transport-https wget  
  
***ProxLB***  
[ProxLB](https://github.com/gyptazy/ProxLB) is a great tool when it comes to Proxmox clusters where it offers many enterprise features that you might already know from the VMware ESX world but are not directly available in Proxmox itself. When it comes to find a suitable node according to memory, cpu or disk configuration for a placement of VMs or CTs or also ongoing re-balancing of the cluster to ensure the ressources are always balanced in a right way, ProxLB finally steps in. Within our pipeline, ProxLB will be used to obtain the best node accroding to the current resource usage in the cluster, to place a new VM object. In the configuration will use memory as the balancing algorythm, where the node with the most free memory will be selected. ProxLB can run on any system that supports Python3. Therefore, we can simply install it on our management system - all further commands will fully use the Proxmox API. It does not need any further ssh access.  
  
You can find th[e ProxLB installation guide here](https://github.com/gyptazy/ProxLB?tab=readme-ov-file#repository) but we will quickly cover all needed actions to install and configure it right here.  
  
   echo "deb [https://repo.gyptazy.ch/](https://repo.gyptazy.ch/) /" &gt; /etc/apt/sources.list.d/proxlb.list  
   wget -O /etc/apt/trusted.gpg.d/proxlb.asc [https://repo.gyptazy.ch/repo/KEY.gpg  
](https://repo.gyptazy.ch/repo/KEY.gpg￼)   apt-get update && apt-get -y install proxlb  
  
Afterwards, ProxLB can be configured. The configuration file is placed in */etc/proxlb/proxlb.con*. For the desired use case, a simple configuration is created:  
  
   \[proxmox\]  
   api\_host: [cluster-vip02.gyptazy.ch](http://cluster-vip02.gyptazy.ch)  
   api\_user: proxlb@pam  
   api\_pass: P@ssw0rd!  
   verify\_ssl: 1  
   \[vm\_balancing\]  
   enable: 1  
   method: memory  
   mode: used  
   \[service\]  
   daemon: 0  
   schedule: 24  
   log\_verbosity: CRITICAL  
  
Afterwards, we can already use ProxLB to get the best node for placing a new VM in our cluster. For such use cases, ProxLB provides the option **\-b** (--best-node) to get the best nodes according to the given configuration setup. It's good to give it a try:  
  
   $&gt; proxlb -b  
   virt01  
  
***Terraform***  
Terraform is an open-source Infrastructure as a Code (IaC) software, developed by Hashicorp. It allows developers and operators to define and provide data center infrastructure using a declarative configuration language. Therefore, we can declare and configure our desired state of new VMs (based on FreeBSD or any other operating system) in our Proxmox cluster infrastructure.  
  
First, we start to install it on our management system just next to ProxLB.  
  
   echo "deb \[signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg\] [https://apt.releases.hashicorp.com](https://apt.releases.hashicorp.com) $(lsb\_release -cs) main" &gt; /etc/apt/sources.list.d/terraform.list  
   wget -O /etc/apt/trusted.gpg.d/terraform.asc [https://apt.releases.hashicorp.com/gpg  
](https://apt.releases.hashicorp.com/gpg￼)   apt-get update && apt-get -y install terraform  
  
We can now use Terraform. For a quick-dive, we can use a pre-configured repository containing all the information and configuration that are required to deploy a VM in a Proxmox cluster by using the BPG Proxmox provider for Terraform.  
  
   mkdir -p ~/training/proxmox/terraform\_deployment  
   cd ~/training/proxmox/terraform\_deployment  
   git clone [https://brew.bsd.cafe/gyptazy/proxmox-terraform-bpg-freebsd-vm  
](https://brew.bsd.cafe/gyptazy/proxmox-terraform-bpg-freebsd-vm￼)   cd proxmox-terraform-bpg-freebsd-vm  
  
This provides us the following directory structure:  
  
   .  
   ├── cloud-init  
   │    └── user-data.yml  
   ├── [files.tf](http://files.tf)  
   ├── [main.tf](http://main.tf)  
   └── virtual\_[machines.tf](http://machines.tf)  
  
  
[*files.tf*](http://files.tf)  
  resource "proxmox\_virtual\_environment\_file" "freebsd14\_cloud" {  
     content\_type = "iso"  
     datastore\_id = "local"  
     node\_name = "virt01"  
     source\_file {  
       path = "[https://cdn.gyptazy.ch/files/amd64/freebsd/images/cloud/freebsd-14.0-ufs-2024-05-04.qcow2"  
](https://cdn.gyptazy.ch/files/amd64/freebsd/images/cloud/freebsd-14.0-ufs-2024-05-04.qcow2"￼)       file\_name = "freebsd-14.0-ufs-2024-05-04.img"  
       checksum = "f472fa7af26aae23fdc92f4a729f083a6a10ce43b1f2049573382bc755c81750"  
     }  
  }  
  
  resource "proxmox\_virtual\_environment\_file" "cloud\_config" {  
     content\_type = "snippets"  
     datastore\_id = "local"  
     node\_name = "virt01"  
     source\_file {  
       path = "cloud-init/user-data.yml"  
     }  
  }  
  
[*main.tf*](http://main.tf)  
  terraform {  
     required\_providers {  
       proxmox = {  
       source = "bpg/proxmox"  
       version = "0.63.0"  
       }  
     }  
  }  
  provider "proxmox" {  
     endpoint = "https:/[cluster-vip02.gyptazy.ch:8006/"  
](http://cluster-vip02.gyptazy.ch:8006/"￼)     username = "root@pam"  
     password = "p4ssw0rd!"  
     insecure = true  
     ssh {  
       agent = false  
     }  
  }  
  
*virtual\_*[*machines.tf*](http://machines.tf)  
  resource "proxmox\_virtual\_environment\_vm" "vm\_object" {  
     name = var.hostname  
     description = "Managed by Terraform"  
     tags = \["terraform"\]  
     node\_name = var.node  
     cpu {  
       cores = 1  
     }  
     memory {  
       dedicated = 2048  
     }  
     agent {  
       enabled = true  
     }  
     network\_device {  
       bridge = "vmbr0"  
     }  
     disk {  
       datastore\_id = "local-lvm"  
       file\_id = proxmox\_virtual\_environment\_file.freebsd14\_[cloud.id](http://cloud.id)  
       interface = "scsi0"  
       size = 32  
     }  
     operating\_system {  
       type = "l26"  
     }  
     serial\_device {}  
     initialization {  
       datastore\_id = "local-lvm"  
       user\_data\_file\_id = proxmox\_virtual\_environment\_[file.cloud](http://file.cloud)\_[config.id](http://config.id)  
       ip\_config {  
         ipv4 {  
           address = "dhcp"  
         }  
       }  
     }  
  }  
  
[*variables.tf*](http://variables.tf)  
  variable "hostname" {  
     type = string  
  }  
  variable "node" {  
     type = string  
  }  
  
*cloud-init/user-data.yml*  
  package\_upgrade: true  
  packages:  
     - qemu-guest-agent  
  
  timezone: Europe/Berlin  
  users:  
     - name: gyptazy  
     passwd: $6$F2NA9T240RZLC7cm$9JcyNJ6vHw0FidyCH7YlDGVE3.4y9K/KtWoCIcP1zb7Kdox/[cPWwudNybjX.km](http://cPWwudNybjX.km)11mmd2El5jDKTe0eLxI7s5l.  
     lock-passwd: false  
     sudo: ALL=(ALL) NOPASSWD:ALL  
     shell: /bin/sh  
     ssh\_authorized\_keys:  
       - ssh-ed25519 AAAAC3NzaF1D4NTE5AAAAIJe45xj6fsadGHesfdadsJFGZxasdfads/gjVmr gyptazy  
  
  power\_state:  
     delay: now  
     mode: reboot  
     message: Rebooting after cloud-init completion  
     condition: true  
  
*Note: The generated password is admin123 and should be replaced! You can also generate your own one with: mkpasswd --method=SHA-512 --rounds=4096*  
  
After setting up the configuration, the Terraform BPG Proxmox provider can simply be installed by running the *init* command. Afterwards, we can directly run the *plan* command to validate the estimated changes on the infrastructure:  
  
   $&gt; terraform init  
   $&gt; terraform plan  
  
If the created plan looks good to you, an initial *apply* can be initiated by simply running the apply command. However, we also want to pass the hostname and the node name to place the VM directly by the cli. Therefore, we can simply add the *\-var* command including our key/values to the apply command:  
  
   $&gt; terraform apply -var="hostname=[managed-vm33.boxybsd.com](http://managed-vm33.boxybsd.com)" -var="node=virt01"  
  
***Automate VM Creation***  
n the world of DevOps, the next step is to automate and integrate all the components you've built. There are various tools available for this, depending on the complexity and requirements of your environment. You might choose Ansible for configuration management, a CI/CD pipeline like ArgoCD or Jenkins for continuous integration and deployment, or even a straightforward shell script for simpler tasks.  
  
In this post, we'll walk through a basic example using a shell script to demonstrate how automation can be implemented. While this is a simple illustration, it serves as a starting point that can be expanded upon. For instance, you could enhance the script to allow dynamic hostname assignment, IP configuration, or even switch between different operating systems.  
  
Our focus here keeps on a [shell script](https://brew.bsd.cafe/gyptazy/proxmox-terraform-bpg-freebsd-vm/src/branch/main/create_vm.sh), showing you how to tie together all the various elements of your infrastructure, giving you the flexibility to manage your environment in a more streamlined and efficient way.  
  
   #!/bin/bash  
  
   PROXMOX\_NODE=$(/bin/proxlb -b)  
   PROXMOX\_VM\_ID=$(  
   echo "Best Proxmox node for next VM placement: $PROXMOX\_NODE"  
   echo "Next VM with ID managed-vm$PROXMOX\_VM\_[ID.boxybsd.com](http://ID.boxybsd.com) will be placed on node: $PROXMOX\_NODE"  
   echo "$((++PROXMOX\_VM\_ID))" &gt; vmcount.txt  
  
   terraform apply -var="hostname=managed-vm$PROXMOX\_VM\_[ID.boxybsd.com](http://ID.boxybsd.com)" -var="node=$PROXMOX\_NODE"  
  
With this very simple shell script we finally reach our goal. Within the first task, ProxLB evaluates the best node in the cluster for a new VM placement. Afterwards, we increment the counter for the numbers, which will create us a dynamic hostname for the VM like [*managed-vmXX.boxybsd.com*](http://managed-vmXX.boxybsd.com), where XX is to be replaced by a uniq and dynamically generated number. Afterwards, Terraform is called in addition to our variables to create the new VM object in the cluster. Of course, this can easily get more complex and can also be integrated into further CI/CD pipelines such like Jenkins, ArgoCD but you can also use Ansible with a dynamic inventory which orchestrates all the things.

  
**Conclusion**  
Imagine a system where creating VMs is as simple as pressing a button—no fuss, no manual work. This solution brings that vision to life, automating the entire process within a Proxmox cluster, and even lending its magic to projects like BoxyBSD. It finds the perfect spot for each new VM, ensuring everything fits just right. Terraform steps in to bring these VMs to life, with names and locations chosen on the fly. A simple shell script is the unseen conductor, ensuring every note is played perfectly. It's a symphony of automation, orchestrating a seamless, efficient, and dynamic creation of virtual worlds.
