4 minutes
Proxmox cloud-init template using ansible
Intro
Creating cloud-init enabled templates is a great and easy way to deploy future nodes in my homelab. I created a small ansible playbook to automatically do this for me using a few variables to customize the image.
Finding the images
Most of the distros have a cloud image that can be used which contains cloud-init already. I only use these kinds of images so that makes my life a little easier. Using variables makes this pretty modular. Example for Arch:
# Image
image_name: Arch-Linux-x86_64-cloudimg.qcow2
image_link: "https://geo.mirror.pkgbuild.com/images/latest/{{ image_name }}"
Creating the playbook
The first parts of the playbook will check if the image
or vm_id
already exist:
- name: Check if image already exists
ansible.builtin.stat:
path: "/home/root/disks/{{ image_name }}"
register: image_location
- name: Check if vm_id already exists
ansible.builtin.shell: "qm list | grep {{ vm_id }} | cut -d ' ' -f 8"
ignore_errors: true
register: id_check
changed_when: false
If the image does not yet exist it will be downloaded:
- name: Download image
ansible.builtin.get_url:
url: "{{ image_link }}"
dest: /home/root/disks/
mode: '0644'
when: not image_location.stat.exists
And once that’s done, the template gets created:
- name: Create template
when: id_check.stdout == ""
block:
- name: Create temp VM
ansible.builtin.command: "{{ item }}"
loop:
- "qm create {{ vm_id }} --name {{ vm_name }} --memory {{ vm_ram }} --net0 {{ vm_network }}"
- "qm importdisk {{ vm_id }} /home/root/disks/{{ image_name }} local-lvm"
- "qm set {{ vm_id }} --scsihw {{ vm_scsihw }} --scsi0 {{ vm_scsi0 }}"
- "qm set {{ vm_id }} --sockets {{ vm_cpu_sockets }} --cores {{ vm_cpu_cores }}"
- "qm set {{ vm_id }} --ide2 {{ vm_ide2 }}"
- "qm set {{ vm_id }} --boot order={{ vm_bootdisk }}"
- "qm set {{ vm_id }} --serial0 {{ vm_serial0 }} --vga {{ vm_vga }}"
- "qm set {{ vm_id }} --ipconfig0 ip={{ vm_ip }}"
- "qm set {{ vm_id }} --ciuser {{ user }}"
- "qm set {{ vm_id }} --sshkeys {{ ssh_key }}"
changed_when: false
- name: Convert to template
ansible.builtin.command: "{{ item }}"
loop:
- "qm resize {{ vm_id }} {{ vm_bootdisk }} {{ vm_disksize }}"
- "qm template {{ vm_id }}"
changed_when: false
Most of the options are using variables, there are even more options for extra customization of the template but I kept it pretty basic.
Full files
Variables:
---
# Default values
# Image
image_name: Arch-Linux-x86_64-cloudimg.qcow2
image_link: "https://geo.mirror.pkgbuild.com/images/latest/{{ image_name }}"
# VM
vm_id: 906
vm_name: archlinux
# Settings
vm_ram: 2048
vm_network: virtio,bridge=vmbr0
vm_cpu_sockets: 1
vm_cpu_cores: 2
vm_scsihw: virtio-scsi-pci
vm_scsi0: "local-lvm:vm-{{ vm_id }}-disk-0"
vm_ide2: local-lvm:cloudinit
vm_boot: C
vm_bootdisk: scsi0
vm_serial0: socket
vm_vga: serial0
vm_ip: dhcp
vm_disksize: 50G
user: rein
ssh_key: "~/.ssh/authorized_keys"
Playbook:
---
# Playbook
- name: Proxmox template
hosts: proxmox
vars_files:
- defaults.yml
tasks:
- name: Make sure image directory exists
ansible.builtin.file:
path: /home/root/disks/
state: directory
mode: '0755'
- name: Check if image already exists
ansible.builtin.stat:
path: "/home/root/disks/{{ image_name }}"
register: image_location
- name: Check if vm_id already exists
ansible.builtin.shell: "qm list | grep {{ vm_id }} | cut -d ' ' -f 8"
ignore_errors: true
register: id_check
changed_when: false
- name: Download image
ansible.builtin.get_url:
url: "{{ image_link }}"
dest: /home/root/disks/
mode: '0644'
when: not image_location.stat.exists
- name: Create template
when: id_check.stdout == ""
block:
- name: Create temp VM
ansible.builtin.command: "{{ item }}"
loop:
- "qm create {{ vm_id }} --name {{ vm_name }} --memory {{ vm_ram }} --net0 {{ vm_network }}"
- "qm importdisk {{ vm_id }} /home/root/disks/{{ image_name }} local-lvm"
- "qm set {{ vm_id }} --scsihw {{ vm_scsihw }} --scsi0 {{ vm_scsi0 }}"
- "qm set {{ vm_id }} --sockets {{ vm_cpu_sockets }} --cores {{ vm_cpu_cores }}"
- "qm set {{ vm_id }} --ide2 {{ vm_ide2 }}"
- "qm set {{ vm_id }} --boot order={{ vm_bootdisk }}"
- "qm set {{ vm_id }} --serial0 {{ vm_serial0 }} --vga {{ vm_vga }}"
- "qm set {{ vm_id }} --ipconfig0 ip={{ vm_ip }}"
- "qm set {{ vm_id }} --ciuser {{ user }}"
- "qm set {{ vm_id }} --sshkeys {{ ssh_key }}"
changed_when: false
- name: Convert to template
ansible.builtin.command: "{{ item }}"
loop:
- "qm resize {{ vm_id }} {{ vm_bootdisk }} {{ vm_disksize }}"
- "qm template {{ vm_id }}"
changed_when: false