--- # Example playbook. The role takes multica_pat as a VALUE — pick ONE of the # lookups below (or any other) and delete the rest. Changing secret provider # later is a change to this file only; the role is untouched. - name: Provision Multica agent hosts hosts: multica_agent_hosts become: true vars: multica_server_url: "https://multica.wdw-labs.cloud" multica_app_url: "https://multica.wdw-labs.cloud" multica_version: "0.6.5" multica_node_version: "24.22.1" multica_workspaces_root: "/opt/multica/workspaces" # No NAS mount under the workspaces root yet, so the mount guard is off. # When utilidor is mounted, switch both lines together — never set the # root onto a mount without also setting the guard, or a play that runs # while the mount is down fills the root filesystem instead of failing. # multica_workspaces_root: "/mnt/utilidor/multica/workspaces" # multica_workspaces_root_mount: "/mnt/utilidor" multica_agent_clis: - claude # --- Secret source ---------------------------------------------------- # This homelab keeps Ansible secrets in ansible-vault encrypted files, so # that is the live form. One PAT per host (named daemon-), held in # the global vault (group_vars/all/vault) as a dictionary keyed by short # hostname: # vault_multica_pats: { haunted-mansion: mul_…, spaceship-earth: mul_… } # A host with no entry fails the pre_task below, by name, before anything # runs. The indirection keeps the name greppable and the file swappable. multica_pat: "{{ vault_multica_pats[inventory_hostname_short] }}" # # Alternatives, if the backend ever consolidates. Each is a one-line # change here; the role is untouched by any of them. # # HashiCorp Vault (community.hashi_vault): # multica_pat: "{{ lookup('community.hashi_vault.vault_kv2_get', # 'multica/agent-host').secret.pat }}" # # 1Password (community.general): # multica_pat: "{{ lookup('community.general.onepassword', # 'Multica Agent Host PAT', field='credential') }}" # # Environment variable on the control node: # multica_pat: "{{ lookup('env', 'MULTICA_PAT') }}" # The role's own PAT assert is no_log, so a missing dictionary entry would # surface there only as "censored". This checks key presence, never the # value, so its failure can name the host. pre_tasks: - name: Require a per-host Multica PAT in the vault ansible.builtin.assert: that: - vault_multica_pats is defined - vault_multica_pats is mapping - inventory_hostname_short in vault_multica_pats fail_msg: >- No Multica PAT for {{ inventory_hostname_short }}: add vault_multica_pats['{{ inventory_hostname_short }}'] to group_vars/all/vault. quiet: true tags: [always, multica_pat_check] roles: - role: multica_agent_host