--- # Phase: Setup and validation for Ansible Vault infrastructure on control node # Concept: This role idempotently prepares the control node to encrypt/decrypt secrets # without embedding plaintext credentials in playbooks or committed files. # On first run, it creates directories and validates prerequisites. # On repeat runs, it verifies infrastructure is still healthy. - name: Create vault infrastructure directory tags: - bootstrap block: # Why ansible.builtin.file instead of 'mkdir'? # - file module is idempotent: runs 100 times, same result # - shell 'mkdir' fails if dir already exists (unless -p flag in separate task) # - file module handles ownership, permissions, and state atomically - name: Ensure vault base directory exists with secure permissions ansible.builtin.file: path: "{{ vault_base_dir }}" state: directory mode: "{{ vault_dir_mode }}" recurse: false register: vault_base_created # Why ansible.builtin.file again (not just the above)? # - Multiple tasks allow clear separation: one for base, one for vars subdir # - Tags can be applied granularly (useful if debugging one phase) - name: Ensure vault encrypted vars subdirectory exists ansible.builtin.file: path: "{{ vault_vars_dir }}" state: directory mode: "{{ vault_dir_mode }}" recurse: false register: vault_vars_created - name: Validate vault prerequisites tags: - validate block: # Include external task file for readability and reusability # - Keeps main.yml focused on the happy path # - Allows the same validation to run standalone for testing - name: Run vault validation checks ansible.builtin.include_tasks: validate.yml vars: skip_validation: "{{ vault_skip_validation }}" - name: Display setup status tags: - bootstrap block: # Why ansible.builtin.debug instead of 'echo' or shell? # - debug module respects Ansible's verbosity levels (-v, -vv) # - Output is properly formatted in Ansible logs and CI/CD systems # - Can be silenced in automated runs, shown in verbose/interactive runs - name: Report vault directory creation status ansible.builtin.debug: msg: | Vault infrastructure ready. Vault base dir: {{ vault_base_dir }} Encrypted vars dir: {{ vault_vars_dir }} Password file expected at: {{ vault_password_file }} Next steps: 1. Create a vault password file (first run only): echo 'your-strong-password' > {{ vault_password_file }} chmod 0600 {{ vault_password_file }} 2. Create your first encrypted vars file: ansible-vault create {{ vault_encrypted_file }} 3. Reference secrets in playbooks: vars: grafana_admin_password: "{{ vault_grafana_admin_password }}" - name: Optional example vault setup tags: - example block: # This block is skipped by default (create_example_vault: false) # Set create_example_vault: true to auto-generate an example encrypted file for learning # Why skip by default? # - Beginners need to understand password generation and encryption manually # - Automated example creation bypasses the learning moment # - Example includes a password_hash() to show Jinja2 + vault integration - name: Create example vault content (for learning, runs with --tags example) ansible.builtin.set_fact: example_vault_content: | --- # Example encrypted vars for Grafana # Reference in playbook with: {{ vault_grafana_admin_password }} vault_grafana_admin_password: "{{ 'change-me-to-strong-password' | password_hash('sha512') }}" vault_authentik_outpost_dozzle_token: "your-authentik-token-here" vault_docker_registry_password: "your-registry-password-here" when: create_example_vault | bool - name: Create example encrypted vars file ansible.builtin.copy: content: "{{ example_vault_content }}" dest: "{{ vault_encrypted_file }}" mode: "{{ vault_file_mode }}" when: create_example_vault | bool register: example_created - name: Encrypt example file with ansible-vault (manual step) ansible.builtin.debug: msg: | To encrypt the example vars file, run manually: ansible-vault encrypt {{ vault_encrypted_file }} Or use the vault password file: ansible-vault encrypt --vault-password-file {{ vault_password_file }} {{ vault_encrypted_file }} when: create_example_vault | bool and example_created is changed