--- - name: Create a Debian VM template and optionally deploy clones hosts: localhost become: true # vars_files: # - "vars/new_vm.yml" tasks: ################################################################## # 1. Ensure Debian GenericCloud Image Exists ################################################################## - name: Check for Debian image ansible.builtin.stat: path: "/var/lib/vz/template/qemu/debian-genericcloud-amd64.qcow2" register: debian_img - name: Download GenericCloud qcow2 ansible.builtin.get_url: url: "https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-amd64.qcow2" dest: "/var/lib/vz/template/qemu/debian-genericcloud-amd64.qcow2" mode: "0644" when: not debian_img.stat.exists ################################################################## # 2. Create Base VM ################################################################## - name: Create VM ansible.builtin.command: > qm create {{ vm_id }} --name {{ hostname }} --memory {{ memory }} --cores {{ cores }} --cpu {{ cpu_type }} --net0 virtio,bridge={{ bridge }},macaddr={{ mac_address }} --agent 1 args: creates: "/etc/pve/qemu-server/{{ vm_id }}.conf" ################################################################## # 3. Optional UEFI + Secure Boot + TPM 2.0 ################################################################## - name: Enable UEFI + Secure Boot + TPM (optional) ansible.builtin.command: > qm set {{ vm_id }} --bios ovmf --efidisk0 {{ storage }}:0,pre-enrolled-keys=1 --tpmstate0 {{ storage }}:1,size=4M,version=v2.0 when: enable_tpm | default(false) ################################################################## # 4. Disk Import ################################################################## - name: Import qcow2 disk ansible.builtin.command: > qm importdisk {{ vm_id }} /var/lib/vz/template/qemu/debian-genericcloud-amd64.qcow2 {{ storage }} - name: Attach imported disk ansible.builtin.command: > qm set {{ vm_id }} --scsihw virtio-scsi-pci --scsi0 {{ storage }}:vm-{{ vm_id }}-disk-0 --format qcow2 - name: Enable serial console + set boot disk # Debian GenericCloud images require serial console enabled, otherwise Cloud-Init may not run fully ansible.builtin.command: > qm set {{ vm_id }} --serial0 socket --boot order=scsi0 ################################################################## # 5. Optional Disk Resize ################################################################## - name: Resize disk ansible.builtin.command: > qm resize {{ vm_id }} scsi0 {{ resize_size }} when: resize_disk | default(false) ################################################################## # 6. Optional GPU Passthrough (Real PCI Device) ################################################################## - name: Enable PCI GPU passthrough (optional) ansible.builtin.command: > qm set {{ vm_id }} --hostpci0 {{ gpu_device }} when: gpu_passthrough | default(false) ################################################################## # 6.1 Optional VirtIO GPU (vGPU) ################################################################## - name: Enable VirtIO GPU (optional) ansible.builtin.command: > qm set {{ vm_id }} --vga virtio when: virtio_gpu | default(false) ################################################################## # 7. Cloud-Init Templates ################################################################## - name: Create Cloud-Init vendor-data ansible.builtin.template: src: cloudinit_vendor.yaml.j2 dest: "/var/lib/vz/snippets/{{ vm_id }}-vendor.yaml" - name: Create Cloud-Init user-data ansible.builtin.template: src: cloudinit_userdata.yaml.j2 dest: "/var/lib/vz/snippets/{{ vm_id }}-user.yaml" - name: Write SSH key snippet copy: content: "{{ lookup('file', ssh_key_path) }}" dest: "/var/lib/vz/snippets/{{ vm_id }}-sshkey.pub" ################################################################## # 8. Apply Cloud-Init ################################################################## - name: Apply Cloud-Init config ansible.builtin.command: > qm set {{ vm_id }} --ciuser {{ ci_user }} --sshkeys local:snippets/{{ vm_id }}-sshkey.pub --hostname {{ hostname }} --citype nocloud --cicustom "user=local:snippets/{{ vm_id }}-user.yaml,vendor=local:snippets/{{ vm_id }}-vendor.yaml" --ipconfig0 {{ ipconfig0 }} ################################################################## # 9. Optional Convert Base VM to Template ################################################################## - name: Convert VM to template ansible.builtin.command: qm template {{ vm_id }} when: make_template | default(false) args: creates: "/etc/pve/qemu-server/{{ vm_id }}.conf.lock" ################################################################## # 10. Optionally Create Cloud-Init Clones from Template ################################################################## - name: Create clones from template (optional) when: create_clones | default(false) loop: "{{ clones }}" loop_control: loop_var: clone block: - name: Clone VM from template ansible.builtin.command: > qm clone {{ vm_id }} {{ clone.id }} --name {{ clone.hostname }} --full {{ clone.full }} creates: "/etc/pve/qemu-server/{{ clone.id }}.conf" - name: Apply cloud-init settings for clone ansible.builtin.command: > qm set {{ clone.id }} --hostname {{ clone.hostname }} --ipconfig0 ip={{ clone.ip }},gw={{ clone.gateway }} - name: Start clone VM ansible.builtin.command: qm start {{ clone.id }}