How to automate provisioning in Proxmox Using Cloud images
Tags: proxmox, cloud, virtualization, cloudinit
Published: 2024-06-16 23:24:56

There are many ways to create virtual machines. In this article, we aim to leverage cloud images as our primary method for automation while provisioning on Proxmox.

## Cloud Images 
Cloud images are pre-configured disk images designed to be used in virtualized environments, such as cloud infrastructure or virtual machine hosts. These images include minimal operating system installation and are optimized for quick deployment and scalability. They have support for quick configuration tools, we will be using cloud-init for our example.

## Script
You'll need a Proxmox Virtual Environment node with SSH access. In our script, we will perform some actions to download and prepare a cloud image.

- Variable and packages
  First, we set up some variables and install some useful packages we will be using in the script.

```bash
### variables
VM_TEMPLATE_ID=999
TEMPLATE_NAME='ubuntu-2204-template'
UBUNTU_IMAGE='ubuntu-22.04-server-cloudimg-amd64-disk-kvm.img'
UBUNTU_IMAGE_QCOW2='ubuntu-22.04.qcow2'
USERNAME='cap'
PASSWORD='12345'
MEMORY='4096'
CPUS='2'

apt update -y && apt install nano wget curl libguestfs-tools -y
```

- Idempotency 
  Run a couple of commands to make our script idempotent. We want a quick way of iteration, so we will be deleting the template in case of changes.

```
# remove old image
rm -rfv ${UBUNTU_IMAGE}

# remove old template container - WILL DESTROY COMPLETELY
qm destroy ${VM_TEMPLATE_ID} --destroy-unreferenced-disks 1 --purge 1
```

- Download the image: 

```
# download new image
wget http://cloud-images.ubuntu.com/releases/22.04/release/${UBUNTU_IMAGE}
```

- Customize the image
  Add the QEMU guest agent to the image so we don't need to install it later. Here, you can use virt-customize to add other tools and have them pre-installed on the image.

```bash
virt-customize -a ${UBUNTU_IMAGE} --install qemu-guest-agent
```
  QEMU guest agent is a daemon installed in the guest and helps us get information about our VMs in the Proxmox environment. See more documentation on [PVE docs](https://pve.proxmox.com/wiki/Qemu-guest-agent).


- Change image extension
Change the img extension on the image file to QCOW2, if you don't rename it in some versions of proxmox qemu will not work

```bash
mv ${UBUNTU_IMAGE} ${UBUNTU_IMAGE_QCOW2}
```
- Resize
 Now we resize the image to a generic size. It doesn't really matter now; we will configure each clone based on the template later.

- Create the vm

  Again, the values for resources here are not really important. Set up a generic value for memory and CPU. We are setting up a network interface. Another key configuration is the storage controller; we use virtio-scsi as it covers more use cases. To learn more about virtualization of 'physical' devices on the virtio family and emulated storage controllers of the virtio family, go to [Virtio](https://docs.oasis-open.org/virtio/virtio/v1.1/virtio-v1.1.html)

```bash
qm create ${VM_TEMPLATE_ID} --memory ${MEMORY} --cores ${CPUS} --net0 virtio,bridge=vmbr0 --name ${TEMPLATE_NAME} --scsihw virtio-scsi-pci
```

After creating the vm we need to configure and convert to a template, so it can be cloned.

- Configure the vm

We will perform in order the following configuration:

- Import the image to a disk and attach to the vm
- attach a cloud-init drive to the vm via ide interface
- Configuring vga output for the console on serial0
- Setting up dhcp for the network so we have connection
- enabling the qemu agent 
- seting default user and password 
- adding ssh keys so we can have ssh access, it will use the same ssh keys from the host in witch you will be executing the script

```bash
qm set ${VM_TEMPLATE_ID} --scsi0 local-lvm:0,import-from=/root/${UBUNTU_IMAGE_QCOW2}
qm set ${VM_TEMPLATE_ID} --ide2 local-lvm:cloudinit
qm set ${VM_TEMPLATE_ID} --boot order=scsi0
qm set ${VM_TEMPLATE_ID} --serial0 socket --vga serial0
qm set ${VM_TEMPLATE_ID} --ipconfig0 ip=dhcp
qm set ${VM_TEMPLATE_ID} --agent enabled=1
qm set ${VM_TEMPLATE_ID} -ciuser ${USERNAME}
qm set ${VM_TEMPLATE_ID} -cipassword ${PASSWORD}
qm set ${VM_TEMPLATE_ID} --sshkeys ~/.ssh/authorized_keys
```

- Converting to template

  In the last step, we convert this VM to a template. A template is a VM with a frozen state used as a base for many machines. It provides a stable starting point and makes it easy for provisioning automation.

```bash
qm template ${VM_TEMPLATE_ID} 
```

  The complete script can be found on my homelab [github](https://github.com/caiocampoos/homelab-k8s)

To run over ssh we can pipe the output via ssh running:


```bash
cat ubuntu-22.04.sh | ssh root@your-PVE-ip /bin/bash
```

Result should be a template on your Proxmox node

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0d4ul5s13832l02af2hh.gif)


  Now you can run any number of copies, we can use qemu set to configure any variable to the clone we want to change from the template.

```bash
# clone

qm clone ${VM_TEMPLATE_ID} ${VM_ID} --name ${VM_NAME}

# configure the vm  
qm set ${VM_ID} --scsi1 local-lvm:40
qm set ${VM_ID} --memory ${MEMORY}
qm set ${VM_ID} --cores ${CPUS}
qm set ${VM_ID} --boot order=scsi0
qm set ${VM_ID} --serial0 socket --vga serial0
qm set ${VM_ID} --ipconfig0 ip=dhcp
qm set ${VM_ID} --agent enabled=1

# start
qm start ${VM_ID}
```

Running again over ssh should create a new vm based on the template:

```bash
cat clone-templates.sh | ssh root@your-PVE-ip /bin/bash
```

Its as fast as it gets, less than 30 seconds and you have a vm running:


![VM](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5c3igfvkwcfvp353nxmr.png)

 

Now we know how to create vms based on templates we can explore more options of remote code execution for provisioning.


Socials: 

[Twitter](https://twitter.com/caiocbrr)
[Linkedin](https://www.linkedin.com/in/caio-campos-borges-rosa-392588143/)
[Github](https://github.com/caiocampoos)


Photo by <a href="https://unsplash.com/@arnosenoner?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash">Arno Senoner</a> on <a href="https://unsplash.com/photos/blue-and-brown-metal-bridge-yqu6tJkSQ_k?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash">Unsplash</a>