The hashicorp.vault collection lets a playbook pull secrets straight out of HashiCorp Vault instead of hardcoding them in a vars file. It was one of 12 new content collections announced for AAP 2.7 at Red Hat Tech Day Netherlands 2026 in Bunnik, alongside OIDC, PKI, dynamic credentials, and Event-Driven Ansible (EDA) integration. Below is a minimal playbook that reads a static secret and requests a short-lived dynamic database credential in the same run.
Example playbook
---
- name: Fetch secrets from Vault
hosts: localhost
gather_facts: false
vars:
vault_addr: "https://vault.internal.example.com:8200"
tasks:
- name: Read a static application secret
community.hashi_vault.vault_kv2_get:
url: "{{ vault_addr }}"
auth_method: token
token: "{{ lookup('env', 'VAULT_TOKEN') }}"
engine_mount_point: secret
path: app/database
register: db_static_secret
- name: Request a dynamic database credential
community.hashi_vault.vault_read:
url: "{{ vault_addr }}"
auth_method: token
token: "{{ lookup('env', 'VAULT_TOKEN') }}"
path: database/creds/readonly-role
register: db_dynamic_creds
- name: Show the dynamic credential lease details
ansible.builtin.debug:
msg: >
Username: {{ db_dynamic_creds.data.data.username }},
lease duration: {{ db_dynamic_creds.data.lease_duration }}s
- name: Configure the app with the dynamic credential
ansible.builtin.template:
src: templates/app-db.conf.j2
dest: /etc/myapp/db.conf
mode: "0600"
vars:
db_user: "{{ db_dynamic_creds.data.data.username }}"
db_password: "{{ db_dynamic_creds.data.data.password }}"
What it does
The first task uses vault_kv2_get to read a plain KV v2 secret at secret/app/database — the kind of static value you'd otherwise store in an encrypted vars file. The second task calls vault_read against a database secrets engine path (database/creds/readonly-role), which Vault generates on demand: every run gets a brand-new username and password with a lease duration instead of a value that sits in Vault forever. The third task just prints the lease so you can see it expire on its own schedule, and the last task writes the freshly minted credential into a config file with tight file permissions.
Authentication here uses a token for simplicity, but hashicorp.vault also supports OIDC login, so a playbook run through AAP 2.7 can authenticate with the platform's own identity provider instead of a long-lived Vault token. Combined with the collection's PKI support for issuing short-lived certificates, and its dynamic-credentials integration with AAP's credential system, the pattern above extends naturally to TLS material, cloud IAM credentials, and other secret types Vault can broker.
Notes / Gotchas
- Install the module dependency first:
ansible-galaxy collection install community.hashi_vaultandpip install hvacon the control node (or execution environment) — the FQCN examples above assume the modules are available undercommunity.hashi_vault. - Never print real secret values with
debugin production; the example above logs metadata only. Mask sensitive output withno_log: trueon any task handling the actual password. - Dynamic credentials expire. If
db_dynamic_creds.data.lease_durationis shorter than your automation window, renew or re-request the lease rather than caching the value across long-running jobs. - Set
VAULT_TOKENvia an AAP credential or environment injection, not in plaintext vars — the whole point of dynamic secrets is to stop hardcoding anything long-lived. - Because the collection also integrates with Event-Driven Ansible, secret rotation events or lease-expiry warnings from Vault can trigger a rulebook that reruns the credential-fetch playbook automatically.
Dynamic secrets turn "here's a password" into "here's a credential that expires," and hashicorp.vault gives Ansible a first-class way to request, use, and discard them inside a single playbook run.
Related Articles
- Ansible Playbook Example: After-Hours Access Report for Branch Offices
- Ansible ansible.platform Collection Example: AAP RBAC as Code
- Ansible Automation Platform 2.7 Changelog: A Field Guide
- AAP 2.7 PostgreSQL 17 and Django 5.2 LTS: What Changed Under the Hood