From 24f34198ff117e10328a03bc3be30940f9e165bc Mon Sep 17 00:00:00 2001 From: Maxim Vershinin Date: Thu, 17 Sep 2026 14:29:23 +0400 Subject: [PATCH] feat(pb,role,task): makes VM deployment playbook use new validation pre_tasks, become_method and os_template tasks are shared now --- playbooks/deploy_vm_from_cloud_init.yaml | 99 ++++--------------- .../deploy_vm_on_proxmox/tasks/create_vm.yaml | 12 +-- roles/deploy_vm_on_proxmox/tasks/main.yaml | 2 +- .../deploy_vm_on_proxmox/tasks/start_vm.yaml | 2 +- tasks/become_method_validation.yaml | 22 +++++ tasks/os_template_validation.yaml | 22 +++++ 6 files changed, 69 insertions(+), 90 deletions(-) create mode 100644 tasks/become_method_validation.yaml create mode 100644 tasks/os_template_validation.yaml diff --git a/playbooks/deploy_vm_from_cloud_init.yaml b/playbooks/deploy_vm_from_cloud_init.yaml index 3053194..9f28a50 100644 --- a/playbooks/deploy_vm_from_cloud_init.yaml +++ b/playbooks/deploy_vm_from_cloud_init.yaml @@ -10,98 +10,33 @@ ansible_user_passwd_hash: "{{ ansible_password | password_hash('sha512', 's3edscrj45e6r') }}" user_passwd_hash: "{{ user_password | password_hash('sha512', 's3ed6123jhgcr') }}" - vars_prompt: - - name: vm_hostname - prompt: "Hostname for the VM (may only contain letters (a-z, A-Z), numbers (0-9), and hyphens)" - default: "new-vm-test" - private: false - - - name: vm_os_template - prompt: "Select OS template. Available: debian-13 | alpine" - default: "debian-13" - private: false - - - name: vm_become_method - prompt: "Select become method. Available: sudo | su | pbrun | pfexec | doas | dzdo | ksu | runas | machinectl" - default: "sudo" - private: false - - - name: vm_create_human_admin_user - prompt: "Create human admin user? (true/false)?" - default: true - private: false - - - name: vm_ip_address - prompt: "IP address for the VM (in x.x.x.x/x formant)" - default: "192.168.0.89/24" - private: false - pre_tasks: - - name: Validate hostname - ansible.builtin.fail: - msg: | - Invalid hostname: "{{ lxc_hostname }}" - Hostname must: - - contain only letters (a-z, A-Z), digits (0-9), hyphens (-), and dots (.) - - not start or end with a hyphen or dot - - have each label between 1 and 63 characters - - not exceed 253 characters in total - when: > - lxc_hostname | length > 253 or - lxc_hostname is not regex('^[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$') - run_once: true - - - name: Validate IP address - ansible.builtin.fail: - msg: | - Invalid IP address: "{{ vm_ip_address }}" - IP address must be in x.x.x.x/x format (e.g., 192.168.0.89/24). - when: not vm_ip_address is regex('^((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)(\\/(3[0-2]|[12]?\\d))?$') - run_once: true - - - name: Validate OS template - ansible.builtin.fail: - msg: | - Invalid OS template: "{{ vm_os_template }}" - Select from available options: debian-13, alpine - when: vm_os_template not in ['debian-13', 'alpine'] - run_once: true + - name: Include input and validation for hostname + ansible.builtin.include_tasks: ../tasks/hostname_validation.yaml - - name: Validate become method - ansible.builtin.fail: - msg: | - Invalid become method: "{{ vm_become_method }}" - Select from available options: sudo | su | pbrun | pfexec | doas | dzdo | ksu | runas | machinectl - when: vm_become_method not in ['sudo', 'su', 'pbrun', 'pfexec', 'doas', 'dzdo', 'ksu', 'runas', 'machinectl'] - run_once: true + - name: Include input and validation for IP + ansible.builtin.include_tasks: ../tasks/ip_validation.yaml + + - name: Include input and validation for human admin user creation + ansible.builtin.include_tasks: ../tasks/human_admin_user_validation.yaml + + - name: Include input and validation for OS template + ansible.builtin.include_tasks: ../tasks/os_template_validation.yaml + + - name: Include input and validation for become method + ansible.builtin.include_tasks: ../tasks/become_method_validation.yaml roles: - ../roles/deploy_vm_on_proxmox tasks: - - name: Make the hostname available to the whole playbook - ansible.builtin.set_fact: - fact_vm_hostname: "{{ vm_hostname }}" - - - name: Make the OS template available to the whole playbook - ansible.builtin.set_fact: - fact_vm_os_template: "{{ vm_os_template }}" - - - name: Make the IP available to the whole playbook - ansible.builtin.set_fact: - fact_vm_ip_address: "{{ vm_ip_address.split('/') | first }}" - - - name: Make the "human admin user" selection available to the whole playbook - ansible.builtin.set_fact: - fact_vm_create_human_admin_user: "{{ vm_create_human_admin_user }}" - - name: Add the target host to the inventory ansible.builtin.add_host: - name: "{{ fact_vm_ip_address }}" + name: "{{ guest_ip_address }}" groups: new_host ansible_user: ansible ansible_become_password: "{{ ansible_become_passwd }}" - ansible_become_method: "{{ vm_become_method }}" + ansible_become_method: "{{ guest_become_method }}" - name: Configure VM @@ -123,10 +58,10 @@ - name: Include human admin user role ansible.builtin.include_role: name: ../roles/human_admin_user - when: "hostvars['localhost']['fact_vm_create_human_admin_user'] | bool" + when: "hostvars['localhost']['create_human_admin_user'] | bool" - name: Include update configs role ansible.builtin.include_role: name: ../roles/update_configs - when: "hostvars['localhost']['fact_vm_create_human_admin_user'] | bool" + when: "hostvars['localhost']['create_human_admin_user'] | bool" diff --git a/roles/deploy_vm_on_proxmox/tasks/create_vm.yaml b/roles/deploy_vm_on_proxmox/tasks/create_vm.yaml index 4de80a0..effc35c 100644 --- a/roles/deploy_vm_on_proxmox/tasks/create_vm.yaml +++ b/roles/deploy_vm_on_proxmox/tasks/create_vm.yaml @@ -10,8 +10,8 @@ api_token_id: ansible api_token_secret: "{{ proxmox_token_secret }}" - clone: "{{ vm_os_template }}-cloud-init-template" - name: "{{ vm_hostname }}" + clone: "{{ guest_os_template }}-cloud-init-template" + name: "{{ guest_hostname }}" storage: main format: qcow2 full: true @@ -33,7 +33,7 @@ api_token_id: ansible api_token_secret: "{{ proxmox_token_secret }}" - name: "{{ vm_hostname }}" + name: "{{ guest_hostname }}" # Cloud-init parameters update: true ide: @@ -41,7 +41,7 @@ ciuser: "ansible" cipassword: "{{ ansible_become_passwd }}" ipconfig: - ipconfig0: "ip={{ vm_ip_address }},gw=192.168.0.1" + ipconfig0: "ip={{ guest_ip_address }}/{{ guest_netmask }},gw=192.168.0.1" - name: Tweak the hardware settings become: false @@ -53,7 +53,7 @@ api_token_id: ansible api_token_secret: "{{ proxmox_token_secret }}" - name: "{{ vm_hostname }}" + name: "{{ guest_hostname }}" cores: 4 memory: 4096 # Minimal value for debian based OS for smooth workflow update: true @@ -68,5 +68,5 @@ api_token_id: ansible api_token_secret: "{{ proxmox_token_secret }}" - name: "{{ vm_hostname }}" + name: "{{ guest_hostname }}" state: started diff --git a/roles/deploy_vm_on_proxmox/tasks/main.yaml b/roles/deploy_vm_on_proxmox/tasks/main.yaml index d54ec6d..7c17320 100644 --- a/roles/deploy_vm_on_proxmox/tasks/main.yaml +++ b/roles/deploy_vm_on_proxmox/tasks/main.yaml @@ -9,7 +9,7 @@ api_token_id: ansible api_token_secret: "{{ proxmox_token_secret }}" - name: "{{ vm_hostname }}" + name: "{{ guest_hostname }}" state: current ignore_errors: true register: vm_status diff --git a/roles/deploy_vm_on_proxmox/tasks/start_vm.yaml b/roles/deploy_vm_on_proxmox/tasks/start_vm.yaml index 8140a72..4bc6f4e 100644 --- a/roles/deploy_vm_on_proxmox/tasks/start_vm.yaml +++ b/roles/deploy_vm_on_proxmox/tasks/start_vm.yaml @@ -10,7 +10,7 @@ api_token_id: ansible api_token_secret: "{{ proxmox_token_secret }}" - name: "{{ vm_hostname }}" + name: "{{ guest_hostname }}" state: started - name: Sleep for 120 seconds to ensure that Proxmox has cloned the VM diff --git a/tasks/become_method_validation.yaml b/tasks/become_method_validation.yaml new file mode 100644 index 0000000..412a7c1 --- /dev/null +++ b/tasks/become_method_validation.yaml @@ -0,0 +1,22 @@ +--- + +- name: Set default become method + ansible.builtin.set_fact: + guest_become_method: "sudo" + +- name: Prompt for become method with immediate validation + ansible.builtin.pause: + prompt: "Choose become method, available: sudo, su, pbrun, pfexec, doas, dzdo, ksu, runas, machinectl [sudo]" + register: become_method_result + until: > + become_method_result.user_input | length == 0 or + ( + become_method_result.user_input in ['sudo', 'su', 'pbrun', 'pfexec', 'doas', 'dzdo', 'ksu', 'runas', 'machinectl'] + ) + retries: 2 + delay: 0 + +- name: Use user input only if provided, otherwise keep the default + ansible.builtin.set_fact: + guest_become_method: "{{ become_method_result.user_input }}" + when: become_method_result.user_input | length > 0 diff --git a/tasks/os_template_validation.yaml b/tasks/os_template_validation.yaml new file mode 100644 index 0000000..27f819b --- /dev/null +++ b/tasks/os_template_validation.yaml @@ -0,0 +1,22 @@ +--- + +- name: Set default OS template + ansible.builtin.set_fact: + guest_os_template: "debian-13" + +- name: Prompt for OS template with immediate validation + ansible.builtin.pause: + prompt: "Choose OS template, available: debian-13, alpine [debian-13]" + register: os_template_result + until: > + os_template_result.user_input | length == 0 or + ( + os_template_result.user_input in ['debian-13', 'alpine'] + ) + retries: 2 + delay: 0 + +- name: Use user input only if provided, otherwise keep the default + ansible.builtin.set_fact: + guest_os_template: "{{ os_template_result.user_input }}" + when: os_template_result.user_input | length > 0