Proxmox VM Deployment Automation

Create new VMs in Proxmox using VM Templates and Ansible in under 2 minutes.

This guide builds upon the previous Ansible tutorial, demonstrating how to leverage Proxmox and Ansible to create and deploy reusable Proxmox templates. This process reduces the time required to spin up a new VM, allowing you to have a ready-to-go machine in under two minutes.

Proxmox Template Creation

Download the Latest ISO Image:

  • Navigate to Storage > ISO Images > Download from URL and download the latest LTS image for Ubuntu or the required distribution.

Create a VM to be Used as a Template:

  • Create a VM as you usually would for a non-template VM:
    • Set the VM's hardware settings and deploy the OS.
    • Install the QEMU guest agent.
    • Create an Ansible account and configure the SSH key.
    • Remove the installation disk after setup.
    • By default, Ubuntu uses Netplan to set DHCP without the MAC address, which means each new VM from the template will have the same IP as the original machine. To change this and issue a new IP to each new templated VM:
      • sudo nano /etc/netplan/50-cloud-init.yaml
      • Add dhcp-identifier: mac under DHCP4: true.
      • Save and exit.
    • Shut down the VM completely.

Create the Template:

  • Right-click on the VM and select Create Template.

Deploy a New VM:

  • Right-click on the template and select Clone.
  • Choose Mode: Full Clone and set the desired name for the new VM.

Using Ansible for VM Setup

After deploying the new VM, you can optionally use Ansible to update the system, change the hostname, and install MeshCentral. The VM template is fully functional as-is, but for any additional configurations beyond the base setup, it is advisable to use Ansible.

Create an Ansible Inventory File

[servers]
NEW_SERVER_IP

[servers:vars]
ansible_user=ansible_user
ansible_ssh_private_key_file=/root/.ssh/id_ed2545219
ansible_become=true
ansible_become_method=sudo
ansible_become_password=password

new_machine

Ansible Playbooks
Create the individual YAML files for different tasks.

  • Connectivity and Update Playbook (new_update.yml)
---
- name: Check connectivity and perform updates
  hosts: servers
  gather_facts: no

  tasks:
    - name: Ping all servers
      ping:

    - name: Update apt cache
      apt:
        update_cache: yes

    - name: Upgrade all packages
      apt:
        upgrade: dist

    - name: Check if upgrades were successful
      shell: dpkg -l
      register: result

    - name: Display upgrade result
      debug:
        var: result.stdout

new_update.yml

  • MeshCentral Deployment Playbook (new_meshcentral.yml)
---
- name: Deploy MeshCentral
  hosts: servers
  become: true
  vars:
    meshcentral_script_url: "https://remote.yourdomain.com/meshagents?script=1"
    meshcentral_server_url: "https://remote.yourdomain.com"
    meshcentral_key: "CONNECTION_KEY"
  tasks:
    - name: Download the MeshCentral install script (direct)
      get_url:
        url: "{{ meshcentral_script_url }}"
        dest: /tmp/meshinstall.sh

    - name: Download the MeshCentral install script (no proxy)
      command: "wget '{{ meshcentral_script_url }}' --no-proxy -O /tmp/meshinstall.sh"

    - name: Make the MeshCentral install script executable
      file:
        path: /tmp/meshinstall.sh
        mode: '0755'

    - name: Run the MeshCentral install script
      command: "/tmp/meshinstall.sh '{{ meshcentral_server_url }}' '{{ meshcentral_key }}'"
      environment:
        https_proxy: ""

new_meshcentral.yml

  • Set Host Name Playbook (new_hostname.yml)
---
- name: Change Hostname Playbook
  hosts: servers
  gather_facts: yes
  become: yes

  vars_prompt:
    - name: new_hostname
      prompt: "Please enter the new hostname"
      private: no

  tasks:
    - name: Set the new hostname
      hostname:
        name: "{{ new_hostname }}"

    - name: Update /etc/hostname
      lineinfile:
        path: /etc/hostname
        line: "{{ new_hostname }}"
        create: yes

    - name: Update /etc/hosts
      lineinfile:
        path: /etc/hosts
        regexp: '^127\.0\.1\.1'
        line: "127.0.1.1 {{ new_hostname }}"
        create: yes
        backrefs: yes

    - name: Reboot the server to apply changes
      reboot:
        msg: "Reboot initiated by Ansible for hostname change"
        pre_reboot_delay: 5
        post_reboot_delay: 30
        reboot_timeout: 600

    - name: Wait for the server to come back
      wait_for_connection:
        delay: 30
        timeout: 300

    - name: Confirm the hostname has been changed
      command: hostname
      register: result

    - name: Verify hostname
      debug:
        msg: "Hostname has been changed to {{ result.stdout }}"
      when: result.stdout == new_hostname        

new_hostname.yml

  • Deploy Docker and Docker Compose (new_docker.yml)
---
- name: Install Docker and Docker Compose
  hosts: all
  become: true

  tasks:
    - name: Update the apt package index
      apt:
        update_cache: yes

    - name: Install Docker
      apt:
        name: docker.io
        state: present

    - name: Start Docker service
      systemd:
        name: docker
        state: started
        enabled: yes

    - name: Install Docker Compose
      apt:
        name: docker-compose
        state: present

new_docker.yml

Combine all the individual playbooks into a master playbook (new_machine_master.yml):

---
- import_playbook: new_hostname.yml
- import_playbook: new_update.yml
- import_playbook: new_meshcentral.yml
- import_playbook: new_docker.yml

new_machine_master.yml

Edit the Inventory File:

  • Update the IP address in the new_machine inventory file:
    • nano new_machine
  • Execute the Master Playbook:
    • ansible-playbook -i new_machine new_machine_master.yml

Creating a New VM from a Template

Whenever a new VM is required, follow these steps to deploy one:

Clone the Template:

  • Right-click on the Proxmox template and select Clone.
  • Choose Mode: Full Clone and set the desired name.

Get the New VM's IP:

  • Wait for the VM to boot up and retrieve its IP address.

Update the Inventory File:

  • Add the new IP address to the servers list in the new_machine inventory file on your Ansible machine:
    • nano new_machine
  • Run the Ansible Playbook:
    • ansible-playbook -i new_machine new_machine_master.yml

Feel free to tweak and expand on these examples as needed for your specific environment.