Managing Virtual Machines with Ansible and Proxmox

Ansible is a very powerful tool and should be in the toolbelt of any Site Reliability or DevOps Engineer. It leverages SSH and does not need any server-side software to run, except python.

I am using Ansible not only to provision virtual machines (VMs) once they are ready and have an SSH connection, but even to provision VMs and templates in the first place.

The following ansible tasks are part of an internal Proxmox collection, that may be published in the not so far future, once it is more polished. For now this code works and is used on a nearly daily basis to manage VMs and build up a completely reproducible environments, that can be recreated from git repositories.

Lets get started with the tasks to create a template. For that purpose we are using cloud-init image from Ubuntu.

---
# tasks file for create_template
- name: Download cloud-init image
  get_url:
    url: "https://cloud-images.ubuntu.com/{{ codename }}/current/{{ codename }}-server-cloudimg-amd64.img"
    dest: "/tmp/{{ codename }}-server-cloudimg-amd64.img"
    # force: true

- name: Create a new cloud template VM ({{ distributor }} {{ codename }} {{ release }})
  shell: |
    qm create {{ vmid }} --memory {{ memory }} --net0 virtio,bridge={{ bridge_interface }} --name "{{ distributor }}-{{ codename }}"
    qm importdisk {{ vmid }} /tmp/{{ codename }}-server-cloudimg-amd64.img local-lvm
    qm set {{ vmid }} --scsihw virtio-scsi-pci --scsi0 local-lvm:vm-{{ vmid }}-disk-0
    qm set {{ vmid }} --ide2 local-lvm:cloudinit
    qm set {{ vmid }} --boot c --bootdisk scsi0
    qm set {{ vmid }} --serial0 socket --vga serial0
    qm template {{ vmid }}    
  args:
    creates: /etc/pve/nodes/{{ proxmox_api_host }}/qemu-server/{{ vmid }}.conf

You may have noticed that the tasks are actually not Proxmox specific. They are using the QEMU/KVM cli tool.

The following role is executed on the Proxmox host and can be executed as follows.

- hosts: pve1
  vars:
    memory: 4096                # 4 GB of memory, can be changed during VM creation
    bridge_interface: vmbr0     # The bridge interface used per default
    release: 20.04              # The Ubuntu release
    codename: focal             # The Ubuntu codename has to match the release
  roles:
    - name: ansible.proxmox.create_template

After we created a template we will use this template to create a new virtual machine. The following lines are again the tasks in the related role.

---
- name: Fail if 'username' and/or 'sshkey' is not set
  fail:
    msg: "Variable 'username' and/or 'sshkey' are not defined.'"
  when: (username is undefined) or (sshkey is undefined)

- name: Copying {{ username }}'s ssh public key to proxmox host
  copy:
    content: '{{ sshkey }}'
    dest: '/tmp/{{ username  }}.pub'
    mode: '0400'

- name: Clone reference template and configure it
  register: clone
  shell: |
    qm clone {{ templateid }} {{ vmid }} --name {{ inventory_hostname }}
    qm set {{ vmid }} --sshkey /tmp/{{ username }}.pub --ciuser {{ username  }}
    qm set {{ vmid }} --ipconfig0 ip={{ ansible_host }}/24,gw={{ gw }} --nameserver {{ nameserver }}
    qm set {{ vmid }} --memory {{ memory }} --cores {{ cpucores }}
    qm set {{ vmid }} --agent 1    
  ignore_errors: true
  args:
    creates: /etc/pve/nodes/{{ proxmox_api_host }}/qemu-server/{{ vmid }}.conf

- name: Resize disk if hdsize defined
  shell: |
    qm resize {{ vmid }} scsi0 +{{ hdsize }}    
  when: clone.changed and hdsize is defined and not clone.failed

- name: Cleanup when failed
  register: cleanup
  shell: qm destroy {{ vmid }} -purge
  when: clone.failed

- name: Start newly created VM
  shell: qm start {{ vmid }}
  when: clone.changed and not clone.failed

And here we have the code snippet that integrates the role into the playbook.

- hosts: "{{ host }}"
  roles:
    - name: ansible.proxmox.create_vm
      vars:
        templateid: 9000
        username: "johndoe"
        sshkey: "ssh-ed25519 AAA... [email protected]"
      delegate_to: pve1

By keeping track of hosts in an inventory file or even dynamically loading them from Proxmox via the Proxmox Inventory we can create a reproducible environment that can be checked into a git repository. Each new host is directly provisioned by Ansible and the related playbook and/or inventory is version controlled.


Author

Alex Oberhauser

Alex Oberhauser is a tech-entrepreneur, innovator and former C-level executive. He is currently working on user controlled identities and the empowerment of the end-users, with privacy and security as part of the value proposition, not as an afterthought.