From 686080a49369edc4e4a8e91ef87b2dd526a26931 Mon Sep 17 00:00:00 2001 From: Harwinder Sekhon Date: Sun, 6 Sep 2026 20:27:50 -0400 Subject: [PATCH] Add repo foundation: proxmox/lxc/vms groups, ansible.cfg, collections, lint configs - inventory: add proxmox host, lxc and vms groups, critical_services per host - retarget bootstrap and time-region to ubuntu:rocky so the Proxmox host is untouched - add ansible.cfg, collections/requirements.yml, .gitignore, .yamllint, .ansible-lint - remove stale root new_lxc.yml duplicate and the Nextcloud upgrade playbook Claude-Session: https://claude.ai/code/session_01JEczyvRjfakkiDNkbZA7PR --- .ansible-lint | 9 ++ .gitignore | 7 ++ .yamllint | 23 ++++++ ansible.cfg | 10 +++ collections/requirements.yml | 7 ++ inventories/all.yml | 42 +++++++++- new_lxc.yml | 75 ----------------- playbooks/add_root_key.yml | 8 +- playbooks/bootstrap_lxc.yml | 4 +- playbooks/health_check.yml | 2 +- playbooks/nextcloud-upgrade.yml | 140 -------------------------------- playbooks/ping.yml | 4 +- playbooks/time-region.yml | 6 +- playbooks/update_system.yml | 3 +- 14 files changed, 111 insertions(+), 229 deletions(-) create mode 100644 .ansible-lint create mode 100644 .gitignore create mode 100644 .yamllint create mode 100644 ansible.cfg create mode 100644 collections/requirements.yml delete mode 100644 new_lxc.yml delete mode 100644 playbooks/nextcloud-upgrade.yml diff --git a/.ansible-lint b/.ansible-lint new file mode 100644 index 0000000..cd6a48a --- /dev/null +++ b/.ansible-lint @@ -0,0 +1,9 @@ +--- +profile: basic + +exclude_paths: + - .claude/ + - collections/ansible_collections/ + +skip_list: + - yaml[line-length] diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..57b7d7a --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +*.retry +__pycache__/ +*.pyc +.vault_pass* +.ansible/ +collections/ansible_collections/ +.claude/ diff --git a/.yamllint b/.yamllint new file mode 100644 index 0000000..46ca2cc --- /dev/null +++ b/.yamllint @@ -0,0 +1,23 @@ +--- +extends: default + +ignore: | + .claude/ + collections/ansible_collections/ + +rules: + line-length: + max: 160 + truthy: + allowed-values: ['true', 'false'] + document-start: + level: warning + braces: + min-spaces-inside: 0 + max-spaces-inside: 1 + comments: + min-spaces-from-content: 1 + comments-indentation: disable + octal-values: + forbid-implicit-octal: true + forbid-explicit-octal: true diff --git a/ansible.cfg b/ansible.cfg new file mode 100644 index 0000000..d4c6169 --- /dev/null +++ b/ansible.cfg @@ -0,0 +1,10 @@ +[defaults] +inventory = inventories/all.yml +host_key_checking = False +retry_files_enabled = False +interpreter_python = auto_silent +callback_result_format = yaml +forks = 10 + +[ssh_connection] +pipelining = True diff --git a/collections/requirements.yml b/collections/requirements.yml new file mode 100644 index 0000000..a8dd8b4 --- /dev/null +++ b/collections/requirements.yml @@ -0,0 +1,7 @@ +--- +# Semaphore installs these automatically before each run +# (it only looks at collections/requirements.yml and roles/requirements.yml). +# Unpinned on purpose until the Semaphore ansible-core version is known. +collections: + - name: community.general + - name: ansible.posix diff --git a/inventories/all.yml b/inventories/all.yml index 2cf5200..b85e2da 100644 --- a/inventories/all.yml +++ b/inventories/all.yml @@ -1,3 +1,17 @@ +# Homelab inventory. +# +# Group layout: +# ubuntu - Ubuntu LXC containers and VMs +# rocky - Rocky Linux 9 LXC containers +# proxmox - the Proxmox VE host +# lxc - the subset of ubuntu/rocky hosts that are LXC containers +# vms - the subset of ubuntu/rocky hosts that are QEMU VMs +# +# A host may belong to several groups. Define ansible_host once, under the OS +# group; lxc/vms list names only. Entries marked PLACEHOLDER need real values. +# +# critical_services is read by playbooks/service_check.yml. +--- all: vars: ansible_user: root @@ -6,15 +20,39 @@ all: hosts: webserver: ansible_host: 192.168.55.10 + critical_services: # PLACEHOLDER: verify service names + - nginx docker: ansible_host: 192.168.55.11 + critical_services: + - docker + - containerd + # ubuntu-vm1: # PLACEHOLDER: first Ubuntu VM, also list under vms + # ansible_host: 192.168.2.XXX + # critical_services: [] rocky: hosts: pihole: ansible_host: 192.168.2.2 + critical_services: + - pihole-FTL bot: ansible_host: 192.168.2.174 + critical_services: [] # PLACEHOLDER: add the bot's service name storage: ansible_host: 192.168.2.115 - # nextcloud: - # ansible_host: 192.168.2.120 \ No newline at end of file + critical_services: [] # PLACEHOLDER: e.g. nfs-server, smb + proxmox: + hosts: + pve: + ansible_host: 192.168.2.254 # PLACEHOLDER: verify + lxc: + hosts: + webserver: + docker: + pihole: + bot: + storage: + vms: + hosts: + # ubuntu-vm1: # PLACEHOLDER: uncomment together with the ubuntu entry above diff --git a/new_lxc.yml b/new_lxc.yml deleted file mode 100644 index 5306549..0000000 --- a/new_lxc.yml +++ /dev/null @@ -1,75 +0,0 @@ ---- -- name: Build Dynamic Rocky Linux LXC Container - hosts: localhost - gather_facts: false - - vars: - # --- Container Specifications --- - lxc_hostname: "new_lxc" - lxc_template: "local:vztmpl/rockylinux-9-default_20240912_amd64.tar.xz" - lxc_cores: 2 - lxc_memory: 1024 - lxc_storage: "TB" - lxc_disk_size: 10 - lxc_bridge: "vmbr0" - lxc_dns: "192.168.2.2" - target_node: "pve" - - # --- Security & Access --- - # The public key to inject into the /root/.ssh/authorized_keys of the new container - lxc_ssh_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINegitHaQG8i+s412h3wTWf/KvpeX1CEpYFva9MhHR+7 ansible-key" - - tasks: - - name: Get the next available VMID from the Proxmox cluster - ansible.builtin.uri: - url: "https://{{ proxmox_api_host }}:8006/api2/json/cluster/nextid" - method: GET - headers: - Authorization: "PVEAPIToken {{ proxmox_api_user }}={{ proxmox_api_password }}" - validate_certs: false - return_content: true - register: nextid_response - - - name: Extract the dynamic VMID from the API response - ansible.builtin.set_fact: - dynamic_vmid: "{{ nextid_response.json.data }}" - - - name: Create the Rocky Linux LXC container - community.general.proxmox: - api_host: "{{ proxmox_api_host }}" - api_user: "{{ proxmox_api_user }}" - api_password: "{{ proxmox_api_password }}" - - node: "{{ target_node }}" - vmid: "{{ dynamic_vmid }}" - hostname: "{{ lxc_hostname }}" - ostemplate: "{{ lxc_template }}" - cores: "{{ lxc_cores }}" - memory: "{{ lxc_memory }}" - storage: "{{ lxc_storage }}" - disk: "{{ lxc_disk_size }}" - - # Configure network for DHCP on the specified bridge - netif: '{"net0":"name=eth0,bridge={{ lxc_bridge }},ip=dhcp"}' - - # Explicitly set the DNS nameserver - nameserver: "{{ lxc_dns }}" - - # Inject the SSH public key - pubkey: "{{ lxc_ssh_key }}" - - # Highly recommended security flag for Proxmox containers - unprivileged: true - state: present - - - name: Ensure the new LXC container is powered on - community.general.proxmox: - api_host: "{{ proxmox_api_host }}" - api_user: "{{ proxmox_api_user }}" - api_password: "{{ proxmox_api_password }}" - vmid: "{{ dynamic_vmid }}" - state: started - - - name: Output the assigned VMID to the Semaphore UI logs - ansible.builtin.debug: - msg: "Successfully built {{ lxc_hostname }} (Rocky Linux 9) with dynamic ID: {{ dynamic_vmid }}" \ No newline at end of file diff --git a/playbooks/add_root_key.yml b/playbooks/add_root_key.yml index 41f68f5..fb6202b 100644 --- a/playbooks/add_root_key.yml +++ b/playbooks/add_root_key.yml @@ -1,15 +1,15 @@ --- - name: Add plain text public SSH key to root user across mixed OS hosts: all - become: yes # Elevates privileges to root via sudo - + become: true + vars: # Paste your PUBLIC key string inside the quotes below - my_plain_text_public_key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDbFKxd6W+gHcNrugkxMwvixGmk3LxtsMEnSdaFp4L78WZ4VhdZrsnSJQ/rViT+vuc6K5SeugtggfQ90L25UyyMv2BhLZaoMS2dpoVJEZon/Y2Hemm11vdEApAK/u0kPTTM5JB0/TZfpoP9sr4pmRIUruRXBhLDvtUV9tJvKSzbhChtB+vBg+MpywG9VvDP6eCVpFaP43RcAFtJR7irJmexd5m4CVhIFaWNeTbbe8YI08i2gDinYl95ygu9i/TorQKLWme2v9ejxQFfJTWGaXHnQ18W0X94Ns4a4OccGcvJ8JmvYsaAPRAOSveq1Fpdw1VXHSKigTmRl6pHWd9/rq6K6Ojctbjo1kHiNHdLKLZbLDeM0OQlzNl74snsImmZddY59wnOKyHtfY0ZI7sQ+7A/0cSaMY6h7lt2xK0WmayuoT8UtKZlItZHCkOA5bV29arI/HTz+yR/LW8SihPPFpVv5aDiOUs0PYd9j0+CtULBJeYpMQJpkD93g06vgEJjhUOBZ6JmilxVDihDARU0x0xiRCQJcq4Nhk/x3GzPe2kkrTI+nZFaQn1K8hcBdSyA12/xAG8Fh1SMCxLwJdCNuL3gQoYd6XPzKqn6BIqAgiyq5DbHhO6mehOye4Rro6i/H1UkZe862QH/R5uH+8Z2/FYRpiCPzQyLwrqx/eVfT7JMFQ== harwinder@harwinder.net" + my_plain_text_public_key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDbFKxd6W+gHcNrugkxMwvixGmk3LxtsMEnSdaFp4L78WZ4VhdZrsnSJQ/rViT+vuc6K5SeugtggfQ90L25UyyMv2BhLZaoMS2dpoVJEZon/Y2Hemm11vdEApAK/u0kPTTM5JB0/TZfpoP9sr4pmRIUruRXBhLDvtUV9tJvKSzbhChtB+vBg+MpywG9VvDP6eCVpFaP43RcAFtJR7irJmexd5m4CVhIFaWNeTbbe8YI08i2gDinYl95ygu9i/TorQKLWme2v9ejxQFfJTWGaXHnQ18W0X94Ns4a4OccGcvJ8JmvYsaAPRAOSveq1Fpdw1VXHSKigTmRl6pHWd9/rq6K6Ojctbjo1kHiNHdLKLZbLDeM0OQlzNl74snsImmZddY59wnOKyHtfY0ZI7sQ+7A/0cSaMY6h7lt2xK0WmayuoT8UtKZlItZHCkOA5bV29arI/HTz+yR/LW8SihPPFpVv5aDiOUs0PYd9j0+CtULBJeYpMQJpkD93g06vgEJjhUOBZ6JmilxVDihDARU0x0xiRCQJcq4Nhk/x3GzPe2kkrTI+nZFaQn1K8hcBdSyA12/xAG8Fh1SMCxLwJdCNuL3gQoYd6XPzKqn6BIqAgiyq5DbHhO6mehOye4Rro6i/H1UkZe862QH/R5uH+8Z2/FYRpiCPzQyLwrqx/eVfT7JMFQ== harwinder@harwinder.net" # yamllint disable-line rule:line-length tasks: - name: Ensure the public key is present in root's authorized_keys ansible.posix.authorized_key: user: root state: present - key: "{{ my_plain_text_public_key }}" \ No newline at end of file + key: "{{ my_plain_text_public_key }}" diff --git a/playbooks/bootstrap_lxc.yml b/playbooks/bootstrap_lxc.yml index b920207..a069b99 100644 --- a/playbooks/bootstrap_lxc.yml +++ b/playbooks/bootstrap_lxc.yml @@ -1,3 +1,4 @@ +--- - name: Bootstrap - Install packages on Ubuntu hosts: ubuntu become: true @@ -78,7 +79,7 @@ update_cache: true - name: Bootstrap - Configure SSH access - hosts: all + hosts: ubuntu:rocky become: true tasks: @@ -114,6 +115,7 @@ line: 'PubkeyAuthentication yes' state: present + # NOTE: playbooks/ssh_harden.yml overrides this via /etc/ssh/sshd_config.d/00-ansible.conf - name: Ensure PasswordAuthentication is enabled ansible.builtin.lineinfile: path: /etc/ssh/sshd_config diff --git a/playbooks/health_check.yml b/playbooks/health_check.yml index 09759b3..f050921 100644 --- a/playbooks/health_check.yml +++ b/playbooks/health_check.yml @@ -59,4 +59,4 @@ ansible.builtin.debug: msg: "WARNING: {{ item.split()[-1] }} usage on {{ item.split()[0] }} of {{ inventory_hostname }}" loop: "{{ disk_usage.stdout_lines }}" - when: (item.split()[-1] | replace('%','') | int) > 85 + when: (item.split()[-1] | replace('%', '') | int) > 85 diff --git a/playbooks/nextcloud-upgrade.yml b/playbooks/nextcloud-upgrade.yml deleted file mode 100644 index 1d7decd..0000000 --- a/playbooks/nextcloud-upgrade.yml +++ /dev/null @@ -1,140 +0,0 @@ ---- -- name: Safe Nextcloud Upgrade Playbook - hosts: nextcloud - become: yes - vars: - nc_path: /var/www/nextcloud - php_bin: /usr/bin/php - - tasks: - - - name: Pause playbook for 3 seconds - pause: - seconds: 3 - prompt: "Press Ctrl+C then A to abort if no backups exist." - - ########################################################################### - # 1 - CHECK CURRENT INSTALLED VERSION - ########################################################################### - - name: 1.1 - Get current NC status - command: "{{ php_bin }} occ status" - args: - chdir: "{{ nc_path }}" - register: nc_status - - - name: 1.2 - Extract installed version - set_fact: - installed_version: "{{ nc_status.stdout | regex_search('versionstring: (.*)', '\\1') }}" - - - name: 1.3 - Show installed version - debug: - msg: "Current installed version: {{ installed_version }}" - - ########################################################################### - # 2 - CHECK LATEST AVAILABLE VERSION (via updater.phar) - ########################################################################### - - name: 2.1 - Check for available update - command: "{{ php_bin }} updater/updater.phar --check" - args: - chdir: "{{ nc_path }}" - register: update_check - failed_when: false # we want to parse output even if exit code != 0 - - - name: 2.2 - Parse latest available version - set_fact: - available_version: "{{ update_check.stdout | regex_search('Update to version (.*) available', '\\1') }}" - when: update_check.stdout is search('Update to version') - - - name: 2.3 - Show available version - debug: - msg: "Latest available version: {{ available_version | default('NONE') }}" - - ########################################################################### - # 2.4 - SKIP IF ALREADY UP TO DATE - ########################################################################### - - name: 2.4 - Skip upgrade if already on latest version - debug: - msg: "Nextcloud is already up to date ({{ installed_version }}). Skipping upgrade." - when: available_version is not defined - tags: skip_upgrade - changed_when: false - - - name: End play if up to date - meta: end_play - when: available_version is not defined - - ########################################################################### - # 3 - DISABLE APPS + MAINTENANCE MODE - ########################################################################### - - name: 3.1 - Enable maintenance mode - command: "{{ php_bin }} occ maintenance:mode --on" - args: - chdir: "{{ nc_path }}" - - ########################################################################### - # 3.2 - RUN UPDATER WITH RETRIES - ########################################################################### - - name: 3.2 - Run Nextcloud CLI updater (retry) - command: "{{ php_bin }} updater/updater.phar --no-interaction" - args: - chdir: "{{ nc_path }}" - register: updater_output - retries: 3 - delay: 5 - until: updater_output.rc == 0 - failed_when: updater_output.rc != 0 - - ########################################################################### - # 3.3 - OCC UPGRADE (DB MIGRATIONS) - ########################################################################### - - block: - - name: 3.3 - Run occ upgrade - command: "{{ php_bin }} occ upgrade" - args: - chdir: "{{ nc_path }}" - register: upgrade_run - failed_when: > - upgrade_run.rc != 0 and - "already in process" not in upgrade_run.stdout and - "maintenance mode" not in upgrade_run.stdout - - rescue: - - name: 3.3R - Handle locked upgrade - debug: - msg: "Upgrade appears stuck. Removing maintenance lock and retrying." - - - name: Remove maintenance mode flag from config.php - lineinfile: - path: "{{ nc_path }}/config/config.php" - regexp: "'maintenance' => true" - line: " 'maintenance' => false," - backrefs: yes - - - name: Retry OCC upgrade - command: "{{ php_bin }} occ upgrade" - args: - chdir: "{{ nc_path }}" - register: upgrade_retry - failed_when: upgrade_retry.rc != 0 - - ########################################################################### - # 4 - ALWAYS DISABLE MAINTENANCE MODE - ########################################################################### - - name: ALWAYS disable maintenance mode - command: "{{ php_bin }} occ maintenance:mode --off" - args: - chdir: "{{ nc_path }}" - ignore_errors: true - - ########################################################################### - # 5 - FINAL RESULT - ########################################################################### - - name: Show final upgrade result - debug: - msg: | - ✔ Nextcloud upgrade completed. - Installed: {{ installed_version }} - Updated to: {{ available_version }} - Check logs if issues appear: - - {{ nc_path }}/data/updater.log - - {{ nc_path }}/data/nextcloud.log diff --git a/playbooks/ping.yml b/playbooks/ping.yml index 3fde6e6..4010e5b 100644 --- a/playbooks/ping.yml +++ b/playbooks/ping.yml @@ -1,7 +1,7 @@ +--- - name: Ping all servers hosts: all - gather_facts: no + gather_facts: false tasks: - name: Test connectivity with ping ansible.builtin.ping: - diff --git a/playbooks/time-region.yml b/playbooks/time-region.yml index a0c4c86..4565ed1 100644 --- a/playbooks/time-region.yml +++ b/playbooks/time-region.yml @@ -1,6 +1,6 @@ --- - name: Configure timezone on all hosts - hosts: all + hosts: ubuntu:rocky become: true gather_facts: true @@ -101,7 +101,7 @@ enabled: true - name: Verify time configuration - hosts: all + hosts: ubuntu:rocky become: true tasks: @@ -112,4 +112,4 @@ - name: Summary ansible.builtin.debug: - msg: "{{ inventory_hostname }} ({{ ansible_virtualization_type | default('unknown') }}) - {{ check_time.stdout }}" \ No newline at end of file + msg: "{{ inventory_hostname }} ({{ ansible_virtualization_type | default('unknown') }}) - {{ check_time.stdout }}" diff --git a/playbooks/update_system.yml b/playbooks/update_system.yml index 4848cef..cecd4a9 100644 --- a/playbooks/update_system.yml +++ b/playbooks/update_system.yml @@ -1,3 +1,4 @@ +--- - name: Check Host Availability hosts: all gather_facts: false @@ -43,4 +44,4 @@ ansible.builtin.dnf: name: "*" state: latest - update_cache: true \ No newline at end of file + update_cache: true