feat(proxmox): automatic vm creation
Signed-off-by: Tuan-Dat Tran <tuan-dat.tran@tudattr.dev>
This commit is contained in:
8
group_vars/proxmox/secrets_vm.yml
Normal file
8
group_vars/proxmox/secrets_vm.yml
Normal file
@@ -0,0 +1,8 @@
|
||||
$ANSIBLE_VAULT;1.1;AES256
|
||||
33333937646463646566653162383830616434336437623065363665323739633331346266333763
|
||||
3364663264306665626465666133666161626333323462650a353366303331303837316133326135
|
||||
33623862333036633438343538633161643333663632303362396438316638626338663935353337
|
||||
3532323337663864640a333765653732393937396561373361393762386565353266343537306161
|
||||
62303539333837666365323630303836373065343437663433616664376432313135636266663764
|
||||
36616132383330656165656264346231323039626131646432323935306233643866366439313962
|
||||
353837396234643739346662316239356134
|
||||
@@ -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"
|
||||
|
||||
@@ -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 }}"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 }}"
|
||||
|
||||
@@ -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 }}"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
127.0.0.1 ansible_connection=local
|
||||
|
||||
[proxmox:children]
|
||||
proxmox_nodes
|
||||
|
||||
[proxmox_nodes]
|
||||
aya01
|
||||
lulu
|
||||
inko
|
||||
|
||||
@@ -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 }}"
|
||||
|
||||
8
roles/proxmox/tasks/00_setup_machines.yml
Normal file
8
roles/proxmox/tasks/00_setup_machines.yml
Normal file
@@ -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
|
||||
7
roles/proxmox/tasks/01_setup_localhost.yml
Normal file
7
roles/proxmox/tasks/01_setup_localhost.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
- name: Install dependencies
|
||||
ansible.builtin.apt:
|
||||
name: "{{ item }}"
|
||||
update_cache: true
|
||||
state: present
|
||||
loop: "{{ proxmox_localhost_dependencies }}"
|
||||
7
roles/proxmox/tasks/05_setup_node.yml
Normal file
7
roles/proxmox/tasks/05_setup_node.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
- name: Install dependencies
|
||||
ansible.builtin.apt:
|
||||
name: "{{ item }}"
|
||||
update_cache: true
|
||||
state: present
|
||||
loop: "{{ proxmox_node_dependencies }}"
|
||||
@@ -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"
|
||||
|
||||
|
||||
6
roles/proxmox/tasks/40_prepare_vm_creation.yml
Normal file
6
roles/proxmox/tasks/40_prepare_vm_creation.yml
Normal file
@@ -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
|
||||
12
roles/proxmox/tasks/42_download_isos.yml
Normal file
12
roles/proxmox/tasks/42_download_isos.yml
Normal file
@@ -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
|
||||
@@ -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"
|
||||
|
||||
30
roles/proxmox/tasks/54_destroy_vm.yml
Normal file
30
roles/proxmox/tasks/54_destroy_vm.yml
Normal file
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
- name: Download Debian Image
|
||||
ansible.builtin.get_url:
|
||||
url: "{{ proxmox_debian_image_url }}"
|
||||
dest: "{{ proxmox_image_path }}"
|
||||
mode: "0644"
|
||||
@@ -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
|
||||
|
||||
@@ -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: []
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user