#SPDX-License-Identifier: MIT-0 --- # tasks file for bookstack # ===================================================================== # 1. VALIDATE REQUIRED SECRETS # ===================================================================== - name: Assert required secrets are set ansible.builtin.assert: that: - bookstack_db_root_password | length > 0 - bookstack_db_password | length > 0 - bookstack_admin_password | length > 0 - (not bookstack_oidc_enabled) or (bookstack_oidc_client_id | length > 0) - (not bookstack_oidc_enabled) or (bookstack_oidc_client_secret | length > 0) - (not bookstack_oidc_enabled) or (bookstack_entra_tenant_id | length > 0) fail_msg: >- One or more required secrets are unset. Provide them via OpenBao lookup, Ansible Vault or --extra-vars. See README for the full list. quiet: true # ===================================================================== # 2. PREPARATION: Packages, directories, APP_KEY # ===================================================================== - name: Ensure required packages are installed ansible.builtin.package: name: - python3-docker - python3-requests state: present - name: Create docker compose directory ansible.builtin.file: path: "{{ bookstack_docker_compose_dir }}" state: directory mode: '0755' - name: Create BookStack data directories ansible.builtin.file: path: "{{ item }}" state: directory owner: "{{ bookstack_puid }}" group: "{{ bookstack_pgid }}" mode: '0755' loop: - "{{ bookstack_docker_volume_dir }}" - "{{ bookstack_appdata_dir }}" - "{{ bookstack_db_data_dir }}" - "{{ bookstack_backup_dir }}" - name: Verify Traefik network exists community.docker.docker_network_info: name: "{{ bookstack_traefik_network }}" register: _traefik_net failed_when: not _traefik_net.exists - name: Check whether APP_KEY has been generated before ansible.builtin.stat: path: "{{ bookstack_docker_volume_dir }}/.app_key" register: _app_key_file - name: Generate persistent APP_KEY on first run ansible.builtin.shell: | set -o pipefail umask 077 echo "base64:$(openssl rand -base64 32)" > {{ bookstack_docker_volume_dir }}/.app_key args: executable: /bin/bash creates: "{{ bookstack_docker_volume_dir }}/.app_key" when: - not _app_key_file.stat.exists - bookstack_app_key | length == 0 - name: Write inventory-provided APP_KEY ansible.builtin.copy: content: "{{ bookstack_app_key }}\n" dest: "{{ bookstack_docker_volume_dir }}/.app_key" mode: '0600' when: - not _app_key_file.stat.exists - bookstack_app_key | length > 0 no_log: true - name: Read APP_KEY back into a fact ansible.builtin.slurp: src: "{{ bookstack_docker_volume_dir }}/.app_key" register: _app_key_slurp no_log: true - name: Register APP_KEY fact ansible.builtin.set_fact: bookstack_resolved_app_key: "{{ _app_key_slurp.content | b64decode | trim }}" no_log: true # ===================================================================== # 3. DEPLOY: Render compose, bring stack up # ===================================================================== - name: Render docker-compose.yml for BookStack ansible.builtin.template: src: docker-compose.yml.j2 dest: "{{ bookstack_docker_compose_dir }}/docker-compose.yml" mode: '0640' notify: restart bookstack - name: Start BookStack containers community.docker.docker_compose_v2: project_src: "{{ bookstack_docker_compose_dir }}" state: present pull: always wait: true # ===================================================================== # 4. CONFIGURE: Wait for app and seed initial admin user # ===================================================================== - name: Wait for BookStack to be ready ansible.builtin.command: cmd: docker exec {{ bookstack_service_name }} curl -sf -o /dev/null -w "%{http_code}" http://localhost/login register: _bookstack_health retries: 30 delay: 5 until: _bookstack_health.stdout == "200" changed_when: false - name: Wait for BookStack migrations to be complete community.docker.docker_container_exec: container: "{{ bookstack_service_name }}-db" argv: - mariadb - --protocol=tcp - -h - 127.0.0.1 - -u - "{{ bookstack_db_user }}" - "-p{{ bookstack_db_password }}" - "{{ bookstack_db_name }}" - -Nse - "SHOW TABLES LIKE 'users';" register: _users_table retries: 30 delay: 5 until: _users_table.stdout | trim == 'users' changed_when: false no_log: true - name: Check whether the initial admin already exists community.docker.docker_container_exec: container: "{{ bookstack_service_name }}-db" argv: - mariadb - --protocol=tcp - -h - 127.0.0.1 - -u - "{{ bookstack_db_user }}" - "-p{{ bookstack_db_password }}" - "{{ bookstack_db_name }}" - -Nse - "SELECT COUNT(*) FROM users WHERE email = '{{ bookstack_admin_email }}';" register: _admin_exists changed_when: false no_log: true - name: Create initial admin user community.docker.docker_container_exec: container: "{{ bookstack_service_name }}" argv: - php - "{{ bookstack_artisan_path }}" - bookstack:create-admin - "--email={{ bookstack_admin_email }}" - "--name={{ bookstack_admin_name }}" - "--password={{ bookstack_admin_password }}" when: (_admin_exists.stdout | trim | int) == 0 no_log: true # ===================================================================== # 5. BACKUP: systemd timer for daily DB + uploads dump # ===================================================================== - name: Render backup script ansible.builtin.template: src: backup.sh.j2 dest: /usr/local/bin/bookstack-backup.sh owner: root group: root mode: '0750' when: bookstack_backup_enabled | bool - name: Render backup systemd service ansible.builtin.template: src: bookstack-backup.service.j2 dest: /etc/systemd/system/bookstack-backup.service mode: '0644' when: bookstack_backup_enabled | bool notify: reload systemd - name: Render backup systemd timer ansible.builtin.template: src: bookstack-backup.timer.j2 dest: /etc/systemd/system/bookstack-backup.timer mode: '0644' when: bookstack_backup_enabled | bool notify: reload systemd - name: Enable and start backup timer ansible.builtin.systemd: name: bookstack-backup.timer enabled: true state: started daemon_reload: true when: bookstack_backup_enabled | bool - name: Disable backup timer when feature is off ansible.builtin.systemd: name: bookstack-backup.timer enabled: false state: stopped when: not (bookstack_backup_enabled | bool) failed_when: false