diff --git a/defaults/main.yml b/defaults/main.yml index 904cbd7..8a5a253 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -1,3 +1,4 @@ +--- vm_id: 150 hostname: debian-template-base @@ -6,48 +7,73 @@ cores: 4 bridge: vmbr0 storage: local-lvm cpu_type: host -mac_address: "DE:AD:BE:EF:44:55" -# Networking for template VM -ip_mode: dhcp -ipconfig0: "ip=dhcp" +# Default MAC generator: avoids collisions +mac_base: "DE:AD:BE" +mac_suffix: "{{ '%02X:%02X' | format((vm_id // 256) % 256, vm_id % 256) }}" +mac_address: "{{ mac_base }}:{{ mac_suffix }}" +############### +# Networking +############### +ip_mode: dhcp # or static +ip_address: "192.168.1.60/24" +gateway: "192.168.1.1" +dns: + - "1.1.1.1" + - "8.8.8.8" + +ipconfig0: "{{ 'ip=dhcp' if ip_mode == 'dhcp' else 'ip=' + ip_address + ',gw=' + gateway }}" + +############### +# Packages +############### packages: - qemu-guest-agent - curl - htop +############### +# Cloud-Init user + SSH + password +############### ci_user: debian -ci_password: "SecurePass123" +ci_password: "SecurePass123" # consider vault ssh_key_path: "~/.ssh/id_rsa.pub" +timezone: "Europe/Berlin" +############### +# Optional Disk Resize +############### resize_disk: true resize_size: "16G" +############### +# GPU Options +############### gpu_passthrough: false gpu_device: "0000:01:00.0" virtio_gpu: false +############### +# TPM + Secure Boot +############### enable_tpm: false -########################## -# NEW OPTIONS -########################## - # Convert VM to template? make_template: true # Create clones from the template? create_clones: true -# List of clones to generate +# List of clones clones: - id: 301 hostname: app01 ip: "192.168.1.81/24" gateway: "192.168.1.1" - + full: 1 - id: 302 hostname: app02 ip: "192.168.1.82/24" gateway: "192.168.1.1" + full: 0 diff --git a/tasks/main.yml b/tasks/main.yml index 3171061..d1ff252 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -1,7 +1,8 @@ --- -- name: Create a Debian VM template and optionally deploy clones +- name: "Create a Debian VM template and optionally deploy clones" hosts: localhost become: true + gather_facts: false tasks: @@ -9,132 +10,158 @@ # 1. Ensure Debian GenericCloud Image Exists ################################################################## - name: Check for Debian image - ansible.builtin.stat: + stat: path: "/var/lib/vz/template/qemu/debian-genericcloud-amd64.qcow2" register: debian_img - name: Download GenericCloud qcow2 - ansible.builtin.get_url: + 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 + # 2. Create Base VM (if not exists) ################################################################## + - name: Check if VM exists + stat: + path: "/etc/pve/qemu-server/{{ vm_id }}.conf" + register: vm_conf + - name: Create VM - ansible.builtin.command: > + command: > qm create {{ vm_id }} --name {{ hostname }} --memory {{ memory }} --cores {{ cores }} --cpu {{ cpu_type }} --net0 virtio,bridge={{ bridge }},macaddr={{ mac_address }} - args: - creates: "/etc/pve/qemu-server/{{ vm_id }}.conf" + --agent 1 + when: not vm_conf.stat.exists ################################################################## - # 3. Optional UEFI + Secure Boot + TPM 2.0 + # 3. Optional UEFI + Secure Boot + TPM ################################################################## - - name: Enable UEFI + Secure Boot + TPM (optional) - ansible.builtin.command: > + - name: Enable UEFI + TPM + command: > qm set {{ vm_id }} --bios ovmf - --efidisk0 {{ storage }}:1,pre-enrolled-keys=1 + --efidisk0 {{ storage }}:0,pre-enrolled-keys=1 --tpmstate0 {{ storage }}:1,size=4M,version=v2.0 when: enable_tpm | default(false) ################################################################## - # 4. Disk Import + # 4. Disk Import & Attach ################################################################## + - name: Check if disk already exists + stat: + path: "/var/lib/vz/images/{{ vm_id }}/vm-{{ vm_id }}-disk-0.qcow2" + register: disk_exists + - name: Import qcow2 disk - ansible.builtin.command: > + command: > qm importdisk {{ vm_id }} /var/lib/vz/template/qemu/debian-genericcloud-amd64.qcow2 {{ storage }} + when: not disk_exists.stat.exists - name: Attach imported disk - ansible.builtin.command: > + command: > qm set {{ vm_id }} --scsihw virtio-scsi-pci --scsi0 {{ storage }}:vm-{{ vm_id }}-disk-0 + when: not disk_exists.stat.exists + + - name: Enable serial console + boot disk + 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 }} + command: qm resize {{ vm_id }} scsi0 {{ resize_size }} when: resize_disk | default(false) ################################################################## - # 6. Optional GPU Passthrough + # 6. Optional GPU ################################################################## - name: PCI GPU passthrough - ansible.builtin.command: > - qm set {{ vm_id }} --hostpci0 {{ gpu_device }} + command: qm set {{ vm_id }} --hostpci0 {{ gpu_device }} when: gpu_passthrough | default(false) - - name: VirtIO GPU passthrough - ansible.builtin.command: > - qm set {{ vm_id }} --vga virtio + - name: VirtIO GPU + command: qm set {{ vm_id }} --vga virtio when: virtio_gpu | default(false) ################################################################## - # 7. Cloud-Init Templates + # 7. Cloud-Init Snippets ################################################################## - name: Create Cloud-Init vendor-data - ansible.builtin.template: + template: src: cloudinit_vendor.yaml.j2 dest: "/var/lib/vz/snippets/{{ vm_id }}-vendor.yaml" - name: Create Cloud-Init user-data - ansible.builtin.template: + 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: > + command: > qm set {{ vm_id }} --ciuser {{ ci_user }} - --cipassword '{{ ci_password }}' - --sshkeys "{{ lookup('file', ssh_key_path) }}" + --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 + # 9. Convert VM to Template ################################################################## - name: Convert VM to template - ansible.builtin.command: qm template {{ vm_id }} + 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 + # 10. Create Clones (if enabled) ################################################################## - - name: Create clones from template (optional) + - name: Create clones from template when: create_clones | default(false) loop: "{{ clones }}" loop_control: loop_var: clone block: + - name: Check if clone exists + stat: + path: "/etc/pve/qemu-server/{{ clone.id }}.conf" + register: clone_conf - name: Clone VM from template - ansible.builtin.command: > - qm clone {{ vm_id }} {{ clone.id }} --name {{ clone.hostname }} + command: > + qm clone {{ vm_id }} {{ clone.id }} --name {{ clone.hostname }} --full {{ clone.full }} + when: not clone_conf.stat.exists - - name: Apply cloud-init settings for clone - ansible.builtin.command: > + - name: Apply Cloud-Init settings for clone + 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 }} + command: qm start {{ clone.id }}