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.