Idempotent LXC with Ansible and Proxmox using “pvesh”

Back a few months when I started my Hetzner deployment of a small Proxmox cluster I checked to see if there was an Proxmox module for Ansible. And indeed there is one on the official documentation but as I was soon to discover, it didn’t work with my Proxmox 6 installation due to issue #59164 which got resolved (but is only available in 2.9.2 which my Debian-based Go.CD agents can’t see right now). Of course, I could install from “pip” sources and that would solve the versioning issue, but back then this was still an issue.

So what I wanted is an idempotent way of creating mostly LXC containers using Proxmox. Initially I wanted to go the REST API way but it was kind of complicated (in the sense of doing that from Ansible code). Secondly, there was the ‘pvesh’ CLI tool that we could use and based on the available “nextid” command I was able to “test” if the declared “vmid” existed:

---
- name: Check it does not exist already
  shell: >-
    pvesh get /cluster/nextid -vmid {{ item.vmid }}
  register: returned
  failed_when: false

- name: Create the pre-configured LXC containers
  shell: >-
    pvesh create /nodes/{{ item.node }}/lxc
    -vmid {{ item.vmid }}
    -hostname '{{ item.hostname }}'
    -storage '{{ item.storage }}'
    -password '{{ lookup ('password', '/dev/null length=15') }}'
    -ostemplate '{{ item.ostemplate }}'
    -memory {{ item.memory }}
    -swap {{ item.swap }}
    -cores {{ item.cores }}
    -onboot {{ item.onboot }}
    -rootfs '{{ item.rootfs }}'
    -net0 '{{ item.net0 }},hwaddr={{ item.hwaddr }}'
    -start {{ item.start }}
    -ssh-public-keys '{{ lookup ('template', 'pubkeys.j2') }}'
  when:
    - returned.rc == 0
# Containers
containers:
  - vmid: 101
    hostname: 'avocado'
    node: 'host0'
    arch: 'amd64'
    onboot: 1
    start: 1
    cores: 2
    memory: 2048
    swap: 0
    storage: 'pool0'
    rootfs: 'pool0:16'
    net0: 'name=eth0,bridge=vmbr0,ip=dhcp,firewall=0'
    hwaddr: 'FE:FE:FE:FE:FE:FE'
    ip: '10.x.y.z'
    ostemplate: 'local:vztmpl/debian-9-turnkey-core_15.0rc1-1_amd64.tar.gz'

The “item” element above as you probably figured it out already is an per-host list of dictionaries declared per-host in the host_vars directory in the Ansible inventory. What I do is to statically map containers to their hosts. The beauty of the ‘pvesh’ tool if you ask me is that it rarely changes or if it does so, it emulates the official API meaning you’d get instant feedback if it failed and you’d probably need few adjustments to make to match the new API. You don’t need the extra dependency of the Ansible proxmox module or to wait for an upstream fix.