HashiCorp Vault part
Start by logging into your account using Vault’s CLI:
export VAULT_ADDR='https://your-vault.example.com:8200'
vault login -method='your_method' username='your_root_account'
# or
export VAULT_TOKEN='hvs.your-token-here'
Enable KV-v2 secrets engine:
vault secrets enable -path=secret kv-v2
Use your created secrets engine and choose a subpath where you want to put secret data:
vault kv put secret/users/user1 username='name' password='secret'
vault kv get secret/users/user1
If you want to delete the secret, use kv delete:
vault kv delete secret/users/user1
Create a policy granting the token read access to the new secret:
vault policy write user1-read-policy - <<EOF
path "secret/data/users/user1" {
capabilities = ["read"]
}
EOF
Please note that Vault’s KV v2 engine has a different path structure for read/write operations. The actual data is stored under secret/data/, while the metadata is under secret/metadata/. This means that when you create policies, you need to reference the correct path for the data you want to access.
Create a token with attached policy:
vault token create -policy=user1-read-policy -orphan=true -period=60m -renewable=false
export VAULT_TOKEN='hvs.your-token-here'
Example output:
[user@rocky10-aarch64 ~]# vault token create -policy=user1-read-policy -orphan=true -period=60m -renewable=false
Key Value
--- -----
token hvs.CAE4615hfcUGxAwdcntfDebxJRC6qKo_Ws1SWEyS9qpf7UpuDGa4KHGh2cy5iT0dWdtN2l4eWFHVlJkQ0lkTzk
token_accessor oAUEhvDjshUyNmOBoOEFvi
token_duration 60m
token_renewable false
token_policies ["default" "user1-read-policy"]
identity_policies []
policies ["default" "user1-read-policy"]
Check the token capabilities:
[user@rocky10-aarch64 ~]# vault token capabilities secret/users/user1
deny
[user@rocky10-aarch64 ~]# vault token capabilities secret/data/users/user1
read
Test data retrieval with the new token:
vault kv get secret/users/user1
Note that the path without ‘data’ is used when using vault kv get.
Ansible part
Create requirements.yml for Ansible collections:
---
collections:
- name: community.hashi_vault
Create a python virtual environment, activate it and install dependencies:
python3 -m venv ~/.venv
source ~/.venv/bin/activate
~/.venv/bin/pip install ansible hvac
Install required collections:
ansible-galaxy collection install -r requirements.yml
Set these two environment variables on the Ansible control node correctly:
echo $VAULT_ADDR # Must point to the vault containing secret data
echo $VAULT_TOKEN # Token you have created using 'vault token create'
Check that VAULT_ADDR contains https://, and that VAULT_TOKEN is set to the token you created in the previous step.
Retrieve and parse the secrets in your playbook:
- name: Install and configure IPA client
hosts: all
gather_facts: true
become: false
remote_user: root
vars:
vault_url: "{{ lookup('env', 'VAULT_ADDR') }}"
vault_token: "{{ lookup('env', 'VAULT_TOKEN') }}"
user1: "{{ lookup('community.hashi_vault.vault_kv2_get',
'users/user1',
url=vault_url,
auth_method='token',
token=vault_token
) }}"
tasks:
- name: Preflight check required env vars
ansible.builtin.assert:
that:
- lookup('env', 'VAULT_ADDR') | length > 0
- lookup('env', 'VAULT_TOKEN') | length > 0
fail_msg: >
Required environment variables or variables are not set.
Ensure VAULT_ADDR, VAULT_TOKEN are defined.
- name: Debug user1 variable
ansible.builtin.debug:
var: user1
- name: Extract IPA credentials
ansible.builtin.set_fact:
ipa_user: "{{ user1.data.data['username'] }}"
ipa_pass: "{{ user1.data.data['password'] }}"
Note the path users/user1 used in the lookup command:
user1: "{{ lookup('community.hashi_vault.vault_kv2_get',
'users/user1',
url=vault_url,
auth_method='token',
token=vault_token
) }}"
This is because the vault_kv2_get lookup is specifically designed for KV-v2 engines. It knows that KV-v2 stores data under mount_point/data/path, so it automatically handles the path translation.
If you have named your KV-v2 anything other than ‘secret’, you must define the path using the engine_mount_point variable.
Example of debug output of user1 variable in ansible:
"user1": {
"data": {
"data": {
"password": "secret",
"username": "name"
},
"metadata": {
"created_time": "2026-05-18T19:59:26.170339039Z",
"custom_metadata": null,
"deletion_time": "",
"destroyed": false,
"version": 1
}
},
"metadata": {
"created_time": "2026-05-18T19:59:26.170339039Z",
"custom_metadata": null,
"deletion_time": "",
"destroyed": false,
"version": 1
},
"raw": {
"auth": null,
"data": {
"data": {
"password": "secret",
"username": "name"
},
"metadata": {
"created_time": "2026-05-18T19:59:26.170339039Z",
"custom_metadata": null,
"deletion_time": "",
"destroyed": false,
"version": 1
}
},
"lease_duration": 0,
"lease_id": "",
"mount_type": "kv",
"renewable": false,
"request_id": "4a87jfc08-1111-fdd3-2766-3bhfv544b6b7d",
"warnings": null,
"wrap_info": null
},
"secret": {
"password": "secret",
"username": "name"
}
}