diff --git a/group_vars/proxmox/secrets_vm.yml b/group_vars/proxmox/secrets_vm.yml new file mode 100644 index 0000000..8c1eb5f --- /dev/null +++ b/group_vars/proxmox/secrets_vm.yml @@ -0,0 +1,8 @@ +$ANSIBLE_VAULT;1.1;AES256 +33333937646463646566653162383830616434336437623065363665323739633331346266333763 +3364663264306665626465666133666161626333323462650a353366303331303837316133326135 +33623862333036633438343538633161643333663632303362396438316638626338663935353337 +3532323337663864640a333765653732393937396561373361393762386565353266343537306161 +62303539333837666365323630303836373065343437663433616664376432313135636266663764 +36616132383330656165656264346231323039626131646432323935306233643866366439313962 +353837396234643739346662316239356134 diff --git a/group_vars/proxmox/vars.yml b/group_vars/proxmox/vars.yml index 9564961..c9f4766 100644 --- a/group_vars/proxmox/vars.yml +++ b/group_vars/proxmox/vars.yml @@ -2,21 +2,18 @@ proxmox_api_user: root proxmox_api_host: 192.168.20.12 proxmox_api_password: "{{ vault.pve.aya01.root.sudo }}" -proxmox_vms: +vms: - name: "test-vm-00" node: "inko" vmid: 950 cores: 2 - cpu: "x86-64-v2-AES" memory: 8192 # in MiB net: net0: "virtio,bridge=vmbr0,firewall=1" - scsi: - scsi0: "proxmox:64,format=qcow2" - scsihw: "virtio-scsi-single" - ostype: "l26" - sshkeys: "{{ pubkey }}" + boot_image: "{{ proxmox_cloud_init_images.ubuntu.name }}" ciuser: "{{ user }}" + sshkeys: "{{ pubkey }}" + disk_size: 32 # in Gb -proxmox_lxcs: +lxcs: - name: "test-lxc-00" diff --git a/host_vars/aya01.yml b/host_vars/aya01.yml index 2b172a1..2443694 100644 --- a/host_vars/aya01.yml +++ b/host_vars/aya01.yml @@ -1,10 +1,10 @@ --- -ansible_user: "root" -ansible_host: 192.168.20.12 -ansible_port: 22 -ansible_ssh_private_key_file: "{{ pk_path }}" -ansible_become_pass: "{{ vault.pve.aya01.root.sudo }}" - -host: - hostname: "aya01" - ip: "{{ ansible_host }}" +# ansible_user: "root" +# ansible_host: 192.168.20.12 +# ansible_port: 22 +# ansible_ssh_private_key_file: "{{ pk_path }}" +# ansible_become_pass: "{{ vault.pve.aya01.root.sudo }}" +# +# host: +# hostname: "aya01" +# ip: "{{ ansible_host }}" diff --git a/host_vars/docker-host00.yml b/host_vars/docker-host00.yml index 74135f1..3a6f8c2 100644 --- a/host_vars/docker-host00.yml +++ b/host_vars/docker-host00.yml @@ -5,7 +5,6 @@ # ansible_port: 22 # ansible_ssh_private_key_file: "{{ pk_path }}" ansible_become_pass: "{{ vault.docker.host00.sudo }}" - -host: - hostname: "docker-host00" - ip: "192.168.20.34" +# host: +# hostname: "docker-host00" +# ip: "192.168.20.34" diff --git a/host_vars/docker-host01.yml b/host_vars/docker-host01.yml index 19c5d36..f8d506a 100644 --- a/host_vars/docker-host01.yml +++ b/host_vars/docker-host01.yml @@ -5,7 +5,7 @@ # ansible_port: 22 # ansible_ssh_private_key_file: "{{ pk_path }}" ansible_become_pass: "{{ vault.docker.host01.sudo }}" - -host: - hostname: "docker-host01" - ip: "192.168.20.35" +# +# host: +# hostname: "docker-host01" +# ip: "192.168.20.35" diff --git a/host_vars/docker-host02.yml b/host_vars/docker-host02.yml index 589ac16..c51ad77 100644 --- a/host_vars/docker-host02.yml +++ b/host_vars/docker-host02.yml @@ -5,7 +5,6 @@ # ansible_port: 22 # ansible_ssh_private_key_file: "{{ pk_path }}" ansible_become_pass: "{{ vault.docker.host02.sudo }}" - -host: - hostname: "docker-host02" - ip: "192.168.20.36" +# host: +# hostname: "docker-host02" +# ip: "192.168.20.36" diff --git a/host_vars/docker-lb.yml b/host_vars/docker-lb.yml index 7024e36..b3fd90b 100644 --- a/host_vars/docker-lb.yml +++ b/host_vars/docker-lb.yml @@ -4,7 +4,6 @@ # ansible_port: 22 # ansible_ssh_private_key_file: "{{ pk_path }}" ansible_become_pass: "{{ vault.docker.lb.sudo }}" - -host: - hostname: "docker-lb" - ip: "192.168.20.37" +# host: +# hostname: "docker-lb" +# ip: "192.168.20.37" diff --git a/host_vars/inko.yml b/host_vars/inko.yml index 32327a4..cc39169 100644 --- a/host_vars/inko.yml +++ b/host_vars/inko.yml @@ -1,10 +1,10 @@ --- -ansible_user: "root" -ansible_host: 192.168.20.14 -ansible_port: 22 -ansible_ssh_private_key_file: "{{ pk_path }}" -ansible_become_pass: "{{ vault.pve.inko.root.sudo }}" - -host: - hostname: "inko" - ip: "{{ ansible_host }}" +# ansible_user: "root" +# ansible_host: 192.168.20.14 +# ansible_port: 22 +# ansible_ssh_private_key_file: "{{ pk_path }}" +# ansible_become_pass: "{{ vault.pve.inko.root.sudo }}" +# +# host: +# hostname: "inko" +# ip: "{{ ansible_host }}" diff --git a/host_vars/lulu.yml b/host_vars/lulu.yml index e490d2f..7802e91 100644 --- a/host_vars/lulu.yml +++ b/host_vars/lulu.yml @@ -1,10 +1,10 @@ --- -ansible_user: "root" -ansible_host: 192.168.20.28 -ansible_port: 22 -ansible_ssh_private_key_file: "{{ pk_path }}" -ansible_become_pass: "{{ vault.pve.lulu.root.sudo }}" - -host: - hostname: "lulu" - ip: "{{ ansible_host }}" +# ansible_user: "root" +# ansible_host: 192.168.20.28 +# ansible_port: 22 +# ansible_ssh_private_key_file: "{{ pk_path }}" +# ansible_become_pass: "{{ vault.pve.lulu.root.sudo }}" +# +# host: +# hostname: "lulu" +# ip: "{{ ansible_host }}" diff --git a/playbooks/proxmox.yml b/playbooks/proxmox.yml index be440f8..f3d9fff 100644 --- a/playbooks/proxmox.yml +++ b/playbooks/proxmox.yml @@ -4,9 +4,10 @@ gather_facts: true vars_files: - secrets.yml + vars: + is_localhost: "{{ inventory_hostname == '127.0.0.1' }}" + is_proxmox_node: "{{ 'proxmox_nodes' in group_names }}" roles: - role: proxmox tags: - proxmox - vars: - action: provision diff --git a/production.ini b/production.ini index deb6855..f908486 100644 --- a/production.ini +++ b/production.ini @@ -2,6 +2,9 @@ 127.0.0.1 ansible_connection=local [proxmox:children] +proxmox_nodes + +[proxmox_nodes] aya01 lulu inko diff --git a/roles/postgres_exporter/templates/postgres_exporter.service.j2 b/roles/postgres_exporter/templates/postgres_exporter.service.j2 index 1c50b77..b4a2f99 100644 --- a/roles/postgres_exporter/templates/postgres_exporter.service.j2 +++ b/roles/postgres_exporter/templates/postgres_exporter.service.j2 @@ -4,7 +4,7 @@ Description=PostgresExporter [Service] TimeoutStartSec=0 User={{ bin_name }} -ExecStart={{ bin_path }} --web.listen-address={{ host.ip }}:{{ bind_port }} {{ options }} +ExecStart={{ bin_path }} --web.listen-address={{ ansible_host }}:{{ bind_port }} {{ options }} Environment="DATA_SOURCE_URI=localhost:5432/postgres?sslmode=disable" Environment="DATA_SOURCE_USER={{ db.user }}" Environment="DATA_SOURCE_PASS={{ db.password }}" diff --git a/roles/proxmox/tasks/00_setup_machines.yml b/roles/proxmox/tasks/00_setup_machines.yml new file mode 100644 index 0000000..ec01b4e --- /dev/null +++ b/roles/proxmox/tasks/00_setup_machines.yml @@ -0,0 +1,8 @@ +--- +- name: Prepare Localhost + ansible.builtin.include_tasks: ./01_setup_localhost.yml + when: is_localhost + +- name: Prepare Localhost + ansible.builtin.include_tasks: ./05_setup_node.yml + when: is_proxmox_node diff --git a/roles/proxmox/tasks/01_setup_localhost.yml b/roles/proxmox/tasks/01_setup_localhost.yml new file mode 100644 index 0000000..17bfc43 --- /dev/null +++ b/roles/proxmox/tasks/01_setup_localhost.yml @@ -0,0 +1,7 @@ +--- +- name: Install dependencies + ansible.builtin.apt: + name: "{{ item }}" + update_cache: true + state: present + loop: "{{ proxmox_localhost_dependencies }}" diff --git a/roles/proxmox/tasks/01_setup_user.yml b/roles/proxmox/tasks/01_setup_user.yml deleted file mode 100644 index e69de29..0000000 diff --git a/roles/proxmox/tasks/05_setup_node.yml b/roles/proxmox/tasks/05_setup_node.yml new file mode 100644 index 0000000..f2d1fed --- /dev/null +++ b/roles/proxmox/tasks/05_setup_node.yml @@ -0,0 +1,7 @@ +--- +- name: Install dependencies + ansible.builtin.apt: + name: "{{ item }}" + update_cache: true + state: present + loop: "{{ proxmox_node_dependencies }}" diff --git a/roles/proxmox/tasks/10_create_secrets.yml b/roles/proxmox/tasks/10_create_secrets.yml index 4feec24..39f48de 100644 --- a/roles/proxmox/tasks/10_create_secrets.yml +++ b/roles/proxmox/tasks/10_create_secrets.yml @@ -23,7 +23,7 @@ - name: Update Vault data ansible.builtin.include_tasks: 15_create_secret.yml - loop: "{{ proxmox_vms | map(attribute='name') }}" + loop: "{{ vms | map(attribute='name') }}" loop_control: loop_var: "vm_name" diff --git a/roles/proxmox/tasks/40_prepare_vm_creation.yml b/roles/proxmox/tasks/40_prepare_vm_creation.yml new file mode 100644 index 0000000..ea03d8f --- /dev/null +++ b/roles/proxmox/tasks/40_prepare_vm_creation.yml @@ -0,0 +1,6 @@ +--- +- name: Download Cloud Init Isos + ansible.builtin.include_tasks: 42_download_isos.yml + loop: "{{ proxmox_cloud_init_images | dict2items | map(attribute='value') }}" + loop_control: + loop_var: distro diff --git a/roles/proxmox/tasks/42_download_isos.yml b/roles/proxmox/tasks/42_download_isos.yml new file mode 100644 index 0000000..428f63d --- /dev/null +++ b/roles/proxmox/tasks/42_download_isos.yml @@ -0,0 +1,12 @@ +--- +- name: Check if file exists + ansible.builtin.stat: + path: "{{ proxmox_dirs.isos }}/{{ distro.name }}" + register: image_stat + +- name: Download image if missing + ansible.builtin.get_url: + url: "{{ distro.url }}" + dest: "{{ proxmox_dirs.isos }}/{{ distro.name }}" + mode: "0644" + when: not image_stat.stat.exists diff --git a/roles/proxmox/tasks/50_create_vms.yml b/roles/proxmox/tasks/50_create_vms.yml index c407f40..d052154 100644 --- a/roles/proxmox/tasks/50_create_vms.yml +++ b/roles/proxmox/tasks/50_create_vms.yml @@ -4,8 +4,14 @@ file: "{{ proxmox_vault_file }}" name: vm_secrets -- name: Create vms - ansible.builtin.include_tasks: 55_create_vm.yml - loop: "{{ proxmox_vms }}" +- name: Destroy vms (Only during rapid testing) + ansible.builtin.include_tasks: 54_destroy_vm.yml + loop: "{{ vms }}" + loop_control: + loop_var: "vm" + +- name: Create vms + ansible.builtin.include_tasks: 55_create_vm.yml + loop: "{{ vms }}" loop_control: loop_var: "vm" diff --git a/roles/proxmox/tasks/54_destroy_vm.yml b/roles/proxmox/tasks/54_destroy_vm.yml new file mode 100644 index 0000000..4b14aea --- /dev/null +++ b/roles/proxmox/tasks/54_destroy_vm.yml @@ -0,0 +1,30 @@ +--- +- name: Gather info about VM + community.general.proxmox_vm_info: + api_user: root@pam + api_password: "{{ vault.pve.aya01.root.sudo }}" + api_host: "192.168.20.12" + vmid: "{{ vm.vmid }}" + register: vm_info + +- name: Stop VM + community.general.proxmox_kvm: + api_user: root@pam + api_password: "{{ vault.pve.aya01.root.sudo }}" + api_host: "192.168.20.12" + node: "{{ vm.node }}" + vmid: "{{ vm.vmid }}" + state: stopped + force: true + when: vm_info.proxmox_vms | length > 0 + +- name: Destroy VM + community.general.proxmox_kvm: + api_user: root@pam + api_password: "{{ vault.pve.aya01.root.sudo }}" + api_host: "192.168.20.12" + node: "{{ vm.node }}" + vmid: "{{ vm.vmid }}" + state: absent + force: true + when: vm_info.proxmox_vms | length > 0 diff --git a/roles/proxmox/tasks/55_create_vm.yml b/roles/proxmox/tasks/55_create_vm.yml index cff235d..76d861d 100644 --- a/roles/proxmox/tasks/55_create_vm.yml +++ b/roles/proxmox/tasks/55_create_vm.yml @@ -1,34 +1,94 @@ +--- - name: Create VM community.general.proxmox_kvm: api_user: root@pam api_password: "{{ vault.pve.aya01.root.sudo }}" api_host: "192.168.20.12" + agent: true name: "{{ vm.name }}" vmid: "{{ vm.vmid }}" node: "{{ vm.node }}" - cpu: "{{ vm.cpu }}" cores: "{{ vm.cores }}" memory: "{{ vm.memory }}" net: "{{ vm.net }}" - scsi: "{{ vm.scsi }}" - scsihw: "{{ vm.scsihw }}" - ostype: "{{ vm.ostype }}" - sshkeys: "{{ vm.sshkeys }}" + scsihw: "virtio-scsi-pci" + ostype: "l26" tags: "{{ proxmox_tags }}" + description: "Created via Ansible with cloud-init" + boot: "order=scsi0" + cpu: "x86-64-v2-AES" ciuser: "{{ vm.ciuser }}" cipassword: "{{ vm_secrets[proxmox_secrets_prefix + '_' + vm.name.replace('-', '_')] }}" - ide: - ide2: "proxmox:cloudinit,format=qcow2" - register: temp + ipconfig: + ipconfig0: "ip=dhcp" + sshkeys: "{{ vm.sshkeys }}" + register: proxmox_deploy_info -- name: Debug temp - ansible.builtin.debug: - msg: "{{ temp }}" - -- name: Set mac +- name: Get MAC Address of new machine ansible.builtin.set_fact: - mac: "{{ temp.mac }}" + mac_address: "{{ proxmox_deploy_info.mac.net0 }}" -- name: debug mac +- name: Import disk + ansible.builtin.shell: | + qm importdisk {{ vm.vmid }} {{ proxmox_dirs.isos }}/{{ vm.boot_image }} {{ proxmox_storage }} + delegate_to: "{{ vm.node }}" + +- name: Attach disk and cloud-init + ansible.builtin.shell: | + qm set {{ vm.vmid }} --scsi0 {{ proxmox_storage }}:{{ vm.vmid }}/vm-{{ vm.vmid }}-disk-0.raw --ide2 {{ proxmox_storage }}:cloudinit --boot order=scsi0 + delegate_to: "{{ vm.node }}" + +- name: Resize scsi0 disk if needed + ansible.builtin.shell: | + qm resize {{ vm.vmid }} scsi0 {{ vm.disk_size }}G + delegate_to: "{{ vm.node }}" + +- name: Start VM + community.general.proxmox_kvm: + api_user: root@pam + api_password: "{{ vault.pve.aya01.root.sudo }}" + api_host: "192.168.20.12" + node: "{{ vm.node }}" + vmid: "{{ vm.vmid }}" + state: started + +- name: Wait for VM to appear on network + ansible.builtin.shell: | + nmap -sn -n -PR 192.168.20.0/24 | grep -B2 "{{ mac_address }}" | grep "Nmap scan report for" + register: vm_nmap_scan + retries: 30 + delay: 5 + until: vm_nmap_scan.stdout != "" + delegate_to: "{{ vm.node }}" + +- name: Extract the IP address from Nmap output + ansible.builtin.set_fact: + vm_found_ip: "{{ vm_nmap_scan.stdout | regex_search('Nmap scan report for ([0-9\\.]+)', '\\1') | first }}" + +- name: Debug IP address ansible.builtin.debug: - msg: "{{ mac }}" + msg: "Found VM IP address: {{ vm_found_ip }}" + +- name: Define SSH config block + ansible.builtin.set_fact: + ssh_entry: | + Host {{ vm.name }} + HostName {{ vm_found_ip }} + Port 22 + User tudattr + IdentityFile /media/veracrypt1/genesis + ProxyJump {{ vm.node }} + +- name: Append new VM to SSH config + ansible.builtin.blockinfile: + path: "{{ ansible_env.HOME }}/.ssh/config_homelab" + marker: "# {mark} HOMELAB VMS BLOCK" + block: | + {{ ssh_entry }} + +- name: Add the new VM to the proxmox_nodes group in production.ini + ansible.builtin.lineinfile: + path: "../inventory.ini" + line: "{{ proxmox_inventory_entry }}" + insertafter: "[proxmox_nodes]" + state: present diff --git a/roles/proxmox/tasks/60_create_container.yml b/roles/proxmox/tasks/60_create_container.yml index b9d4168..e1a29a0 100644 --- a/roles/proxmox/tasks/60_create_container.yml +++ b/roles/proxmox/tasks/60_create_container.yml @@ -6,6 +6,6 @@ - name: Create vms ansible.builtin.include_tasks: 65_create_container.yml - loop: "{{ proxmox_lxcs }}" + loop: "{{ lxcs }}" loop_control: loop_var: "container" diff --git a/roles/proxmox/tasks/90-download-image.yml b/roles/proxmox/tasks/90-download-image.yml deleted file mode 100644 index f6222c6..0000000 --- a/roles/proxmox/tasks/90-download-image.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -- name: Download Debian Image - ansible.builtin.get_url: - url: "{{ proxmox_debian_image_url }}" - dest: "{{ proxmox_image_path }}" - mode: "0644" diff --git a/roles/proxmox/tasks/main.yml b/roles/proxmox/tasks/main.yml index b7e78c8..d0b3422 100644 --- a/roles/proxmox/tasks/main.yml +++ b/roles/proxmox/tasks/main.yml @@ -1,12 +1,18 @@ --- -- name: Setup user - ansible.builtin.include_tasks: 01_setup_user.yml +- name: Prepare Machines + ansible.builtin.include_tasks: 00_setup_machines.yml - name: Create VM vault ansible.builtin.include_tasks: 10_create_secrets.yml + when: is_localhost + +- name: Prime node for VM + ansible.builtin.include_tasks: 40_prepare_vm_creation.yml + when: is_proxmox_node - name: Create VMs ansible.builtin.include_tasks: 50_create_vms.yml - + when: is_localhost - name: Create LXC containers ansible.builtin.include_tasks: 60_create_containers.yml + when: is_localhost diff --git a/roles/proxmox/vars/main.yml b/roles/proxmox/vars/main.yml index 1587e21..17ce0bc 100644 --- a/roles/proxmox/vars/main.yml +++ b/roles/proxmox/vars/main.yml @@ -1,10 +1,25 @@ -author: tuan-dat.tran@tudattr.dev -creator: ansible +proxmox_author: tuan-dat.tran@tudattr.dev +proxmox_creator: ansible + +proxmox_storage: proxmox proxmox_vault_file: ../group_vars/proxmox/secrets_vm.yml proxmox_secrets_prefix: secrets_vm -proxmox_debian_image_url: https://cdimage.debian.org/images/cloud/bookworm/20250316-2053/debian-12-genericcloud-amd64-20250316-2053.qcow2 -proxmox_image_path: /opt/template/iso/ +proxmox_cloud_init_images: + debian: + name: debian-12-genericcloud-amd64.qcow2 + url: https://cdimage.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-amd64.qcow2 + ubuntu: + name: noble-server-cloudimg-amd64.img + url: https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img +proxmox_dirs: + isos: /opt/proxmox/template/iso/ proxmox_tags: - - "{{ creator }}" + - "{{ proxmox_creator }}" + +proxmox_node_dependencies: + - libguestfs-tools + - nmap + +proxmox_localhost_dependencies: [] diff --git a/roles/reverse_proxy/templates/Caddyfile.j2 b/roles/reverse_proxy/templates/Caddyfile.j2 index 1c52c80..9c7faaa 100644 --- a/roles/reverse_proxy/templates/Caddyfile.j2 +++ b/roles/reverse_proxy/templates/Caddyfile.j2 @@ -10,7 +10,7 @@ {% if http_port %} {{ service.name }}.{{ domain }} { {% for vm in service.vm %} - reverse_proxy {{ hostvars[vm].host.ip }}:{{ http_port[0] }} + reverse_proxy {{ hostvars[vm].ansible_host }}:{{ http_port[0] }} {% endfor %} log { output file /var/log/caddy/{{ service.name }}.log