--- - name: Install and Configure Incus (Stable) on Localhost hosts: localhost connection: local become: true vars: # ==> User Configuration # The user to grant Incus admin privileges. # 'ansible_user' resolves to the user running the playbook (e.g., 'ubuntu', 'your_user'). incus_admin_user: "{{ ansible_user }}" # ==> Incus Initialization (Preseed) Configuration # Name for the default storage pool incus_storage_pool_name: "default" # Storage driver. 'zfs' is recommended. 'dir' is a fallback. incus_storage_driver: "zfs" # Network bridge name incus_network_bridge: "incusbr0" # IPv4 address and subnet for the bridge incus_ipv4_addr: "10.0.5.1/24" # IPv6 address and subnet for the bridge incus_ipv6_addr: "fd42:5c6b:76c3:513::1/64" tasks: - name: 1. Install prerequisite packages ansible.builtin.apt: name: - curl - gnupg - ufw # Ensure ufw is installed for firewall rules state: present update_cache: true - name: 2. Create APT keyrings directory ansible.builtin.file: path: /etc/apt/keyrings state: directory mode: '0755' - name: 3. Add Zabbly GPG key ansible.builtin.get_url: url: https://pkgs.zabbly.com/key.asc dest: /etc/apt/keyrings/zabbly.asc mode: '0644' force: true - name: 4. Add Zabbly Incus Stable repository ansible.builtin.template: src: templates/zabbly-incus-stable.sources.j2 dest: /etc/apt/sources.list.d/zabbly-incus-stable.sources mode: '0644' notify: Update APT Cache - name: Handler to update APT cache meta: flush_handlers - name: 5. Install Incus packages (with ZFS) ansible.builtin.apt: name: - incus - incus-client - incus-ui-canonical - zfsutils-linux state: present when: incus_storage_driver == 'zfs' - name: 5. Install Incus packages (non-ZFS) ansible.builtin.apt: name: - incus - incus-client - incus-ui-canonical state: present when: incus_storage_driver != 'zfs' - name: 6. Add user to the 'incus-admin' group ansible.builtin.user: name: "{{ incus_admin_user }}" groups: incus-admin append: yes - name: 7. Template the Incus preseed configuration file ansible.builtin.template: src: templates/incus-preseed.yml.j2 dest: /tmp/incus-preseed.yml mode: '0600' - name: 8. Initialize Incus using preseed configuration ansible.builtin.command: cmd: incus admin init --preseed < /tmp/incus-preseed.yml args: # This task will only run if the storage pool doesn't already exist, making it idempotent. creates: /var/lib/incus/storage-pools/{{ incus_storage_pool_name }} - name: 9. Configure Incus to listen for Web UI access ansible.builtin.command: cmd: incus config set core.https_address :8443 changed_when: false # This command is idempotent - name: 10. Configure UFW firewall rules for Incus bridge community.general.ufw: rule: "{{ item.rule }}" direction: "{{ item.direction | default(omit) }}" interface: "{{ incus_network_bridge }}" route: "{{ item.route | default(omit) }}" loop: - { rule: 'allow', direction: 'in' } - { rule: 'allow', route: 'yes', direction: 'in' } - { rule: 'allow', route: 'yes', direction: 'out' } - name: Display post-installation instructions ansible.builtin.debug: msg: - "Incus installation and configuration is complete!" - "IMPORTANT: To manage Incus without 'sudo', you must LOG OUT and LOG BACK IN, or run 'newgrp incus-admin' in your current shell." - "Access the Web UI at: https://localhost:8443" handlers: - name: Update APT Cache ansible.builtin.apt: update_cache: true