Ansible Proxmox Vault Integration

This repository provides an Ansible configuration for managing Proxmox VMs and LXCs, with:


Prerequisites

Before using this repository, ensure the following are set up:

Requirement Details
HashiCorp Vault A running Vault instance with AppRole authentication enabled. You need a Role ID and Secret ID with read access to the secret paths used (e.g., ifri/data/infra/proxmox/ansibledo-api-auth).
Proxmox API Token A Proxmox API token (or user/password) with permissions to list VMs/LXCs, read facts, and execute pct exec for nosshd containers. The token is stored in Vault (not in this repo).
ansibledo user A non‑root user (e.g., ansibledo) that exists on all managed hosts (VMs and LXCs) with passwordless sudo configured (/etc/sudoers.d/ansibledo). The SSH public key must be deployed for SSH‑based connections.
GNOME Keyring Installed and running (secret-tool available). Used to store the ansible-vault passwords for the vault identities ansiblevid-apps and ansiblevid-infra.
Python 3.11+ With venv support.
Ansible Collections community.hashi_vault (≥7.0) and community.proxmox (≥1.0) must be installed.

What’s Inside

File / Directory Purpose
ansible.cfg Main configuration: inventories, SSH settings, and vault_identity_list pointing to the GNOME Keyring script.
ansible.cfg.d/gnome-keyring-client.sh Script to retrieve ansible-vault passwords from GNOME Keyring.
ansible.cfg.d/inventory-conf.proxmox.yml Dynamic inventory plugin config for Proxmox – uses HashiCorp Vault for token lookup.
ansible.cfg.d/requirements.txt Python dependencies (Ansible 12, hvac, etc.).
ansible.cfg.d/requirements.yaml Ansible Galaxy collections (e.g., community.proxmox).
inventory/hosts.ini Static inventory fallback (localhost, static groups).
inventory/group_vars/all/hvault.yaml Vault connection settings (AppRole).
inventory/host_vars/localhost/env.yaml Local Python interpreter (uses virtualenv).
.envrc Environment variables for HashiCorp Vault auth (source before running).

Two Separate “Vaults”

GNOME Keyring (local) HashiCorp Vault (remote)
Stores the ansible-vault password for encrypting YAML variables. Stores runtime secrets (Proxmox tokens, DB passwords).
Accessed via vault_identity_list in ansible.cfg and secret-tool. Accessed via lookup('community.hashi_vault.hashi_vault', ...).
Configured once: secret-tool store ... Configured via VAULT_ADDR, VAULT_ROLE_ID, VAULT_SECRET_ID in .envrc.

Smart Inventory Logic

The dynamic inventory (inventory-conf.proxmox.yml) composes ansible_host and ansible_connection based on the LXC tag:

compose:
  ansible_host: "'prox68' if ( proxmox_vmtype == 'lxc' and 'nosshd' in (proxmox_tags_parsed|list) ) else ( proxmox_ipconfig0.ip.split('/')[0] if proxmox_ipconfig0.ip is defined else proxmox_net0.ip.split('/')[0] )"
  ansible_connection: "'community.proxmox.proxmox_pct_remote' if ( proxmox_vmtype == 'lxc' and 'nosshd' in (proxmox_tags_parsed|list) ) else 'ssh'"
  ansible_user: "'ansibledo'"

With sshd tag or VM → SSH connection using the guest IP. With nosshd tag (LXC only) → Proxmox API connection via pct exec (no SSH needed).

Getting Started

source ~/.venvs/ansible12/bin/activate

Store ansible-vault passwords in GNOME Keyring (for identities ansiblevid-apps and ansiblevid-infra):

secret-tool store --label='Ansible Vault - INFRA' application ansible-vault vault-id ansiblevid-infra username $USER
secret-tool store --label='Ansible Vault - APPS'  application ansible-vault vault-id ansiblevid-apps  username $USER

Set HashiCorp Vault environment (source .envrc with your actual AppRole credentials).

HashiCorp Vault Secret Lookup

Needed vars for all hosts

cat  inventory/group_vars/all/hvault.yaml 
---
vault_addr: "https://vault.ifri.akl"
vault_auth_method: "approle"
vault_role_id: "{{ lookup('env', 'VAULT_ROLE_ID') }}"
vault_secret_id: "{{ lookup('env', 'VAULT_SECRET_ID') }}"
vault_validate_certs: true

# Pre-built auth string for hashi_vault lookups
_vault_auth: "url={{ vault_addr }} auth_method={{ vault_auth_method }} role_id={{ vault_role_id }} secret_id={{ vault_secret_id }} validate_certs={{ vault_validate_certs }}"

Lookup example:

$ ansible localhost -m debug -a "msg={{ lookup('community.hashi_vault.hashi_vault', 'secret=ifri/data/infra/proxmox/ansibledo-api-auth').token_secret }}"
localhost | SUCCESS => {
    "msg": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}

Explanation: The Proxmox API token for ansibledo@pve user is retrieved securely from HashiCorp Vault – no secret is stored in the inventory or Git.

Examples

Example 1: LXC with SSH Enabled (sshd tag) izlan13 has the sshd tag – Ansible connects via SSH using the container’s IP (192.168.1.18).

$ ansible-inventory --host izlan13 | egrep -i "ansible_host|ansible_connection|tag"
"ansible_connection": "ssh",
"ansible_host": "192.168.1.18",
"proxmox_description": "Created by OpenTofu. Tags: debian13, sshd. TemplateID 13010\nlxc.apparmor.profile: unconfined\n",
"proxmox_tags": "debian13;sshd",
"proxmox_tags_parsed": [

Example 2: LXC Without SSH (nosshd tag) reman13 has the nosshd tag – Ansible connects via the Proxmox API (pct exec) through the Proxmox host prox68. No SSH daemon is required in LXC container.

$ ansible-inventory --host reman13 | egrep -i "ansible_host|ansible_connection|tag"
"ansible_connection": "community.proxmox.proxmox_pct_remote",
"ansible_host": "prox68",
"proxmox_description": "Created by OpenTofu. Tags: debian13, nosshd. TemplateID 13012\n",
"proxmox_tags": "debian13;nosshd",
"proxmox_tags_parsed": [