ansible-collections / community.hashi_vault

Ansible collection for managing and working with HashiCorp Vault.
https://docs.ansible.com/ansible/devel/collections/community/hashi_vault/index.html
GNU General Public License v3.0
81 stars 58 forks source link

Lookup plugin equivalent for the module `vault_kv2_write` #373

Closed wrouesnel closed 1 year ago

wrouesnel commented 1 year ago
SUMMARY

Lookup plugin equivalent for the module vault_kv2_write

ISSUE TYPE
COMPONENT NAME

vault_kv2_write

ADDITIONAL INFORMATION

A matching lookup plugin equivalent for vault_kv2_write is desirable to have to take full advantage of the Check-and-Set functionality for automatically configuring default passwords.

In the vast majority of cases, generating any password in Vault should happen as soon as a playbook is invoked so that the data can be handled like any other inventory variable.


admin_account: root
admin_password: '{{ lookup("vault_kv2_write", cas=0, path='admin_passwords/' + inventory_hostname, data={"password": lookup("random_string") } ) }}'

The above stanza would effectively ensure first execution of the playbook sets up a new password if none exists, but otherwise not touch it.

briantist commented 1 year ago

Hi @wrouesnel , welcome!

There aren't any plans to include a lookup for vault_kv2_write and I don't think I would approve a PR for it.

The short answer why is that we generally want to avoid changing state (writes) in lookups, because they don't respect check mode and are lazily evaluated.

In your example, that lookup will not be executed "as soon as a playbook is invoked"; it will be executed the first time it needs to be, and actually every time it's references thereafter (unless it's set by set_fact or something). It will also execute once per host (though that seems to be intentional in this case).

For a more detailed explanation of why lookups are tricky and how that compounds when changing state, have a look at: https://docs.ansible.com/ansible/devel/collections/community/hashi_vault/docsite/lookup_guide.html

Using cas=0 will cause it to succeed only if the secret doesn't exist and it will otherwise cause an exception unless you also added errors='ignore'.

Additionally, the write operation does not return the secret, so your admin_password variable will not contain what you need.


The module version can be used to do what you want, if you put it early in the list of tasks, or in pre_tasks to do it before roles. The module allows you more control too:

Here's what this might look like (not tested):

- hosts: all
  vars:
    admin_account: root
    admin_pw_path: "admin_passwords/{{ inventory_hostname }}"
  pre_tasks:
    - name: try to retrieve the secret
      register: admin_pw_get
      ignore_errors: true
      delegate_to: localhost
      community.hashi_vault.vault_kv2_get:
        path: '{{ admin_pw_path }}'

    - name: set the admin password
      ansible.builtin.set_fact:
        admin_password: "{{ admin_pw_get.secret.password | default(lookup('random_string')) }}"

    - name: set the password in Vault
      when: admin_pw_get is failed
      delegate_to: localhost
      check_mode: false
      community.hashi_vault.vault_kv2_write:
        cas: 0
        path: '{{ admin_pw_path }}'
        data:
          password: '{{ admin_password }}'
  roles:
    - role1
    - role2
  tasks:
    - some_task: