diff --git a/README.md b/README.md index 8a3395b..30eaf47 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,96 @@ # role_include_vault-check -An include-only Ansible role to check if HashiCorp Vault variables truly exist \ No newline at end of file +An include-only Ansible role to check if HashiCorp Vault variables truly exist + +# Import + +Whenever you work with HashiCorp Vault data stored in a `kv` secrets engine you may want to import this role into whatever playbook you're running. + +Given a `role` directory create a `meta` subdirectory underneath it with a file named `requirements.yml`. + +``` +role +├── meta <--- Create this +│   └── requirements.yml <--- Create this +└── tasks + └── main.yml +``` + +In `requirements.yml` add: + +``` +- src: "https://quico.space/quico-ansible/role_include_vault-check.git" + version: "master" +``` + +Now whenver you import `role` for example via `ansible-galaxy install ...` you'll automatically get this one downloaded as well. You can optionally leave out `version: "master"` since this is the default version anyways, meaning the `role_include_vault-check` newest master commit. The `version:` attribute helps you pin a version, for example as `version: "v1.0.0"` which will instead pull `role_include_vault-check` Git tag `v1.0.0`. Side note, this role follows the [Semantic Versioning](https://semver.org/) standard. A Git tag name `v1.0.0` refers to Semantic Version `1.0.0`. + +# Use it + +## Generic setup + +From your `role` call this one like so: + +``` +- name: "If a secret is missing: Fail progress" + import_role: + name: "role_include_vault-check" + vars: + - vault_check_base_path: "{{ vault_check_base_path }}" + - vault_check_inc_vault_data: "{{ vault_check_vault_data }}" + - vault_check_fail_check: + - "password" + - "password_salt" +``` + +This `role_include_vault-check` expects two variables in your `import_role` task for example via the `vars` statement: + +1. `vault_check_base_path`: The path in HashiCorp Vault's `kv` secrets engine where secrets are located. Has cosmetic purpose only to inform the user where a key-value check succeeded or failed. + +1. `vault_check_fail_check`: A list of keys located at `vault_check_base_path` for which you want to confirm that they are non-empty. + + Can either be defined in place like so: + ``` + - vault_check_fail_check: + - "password" + - "password_salt" + ``` + + Or can use a list variable defined elsewhere: + ``` + - vault_check_fail_check: "{{ some_list }}" + ``` + +## In context + +In a real-world use case you'll likely first query HashiCorp Vault for key-value pairs for example like so: + +``` +- name: "Get secrets" + no_log: "true" + loop_control: + loop_var: "server" + with_community.hashi_vault.vault_kv2_get: + - "some/vault/kv/path/password" + - "some/vault/kv/path/password_salt" + ansible.builtin.set_fact: + vault_data: "{{ vault_data | default({}) | combine (server.secret) }}" +``` + +The `vault_kv2_get` lookup plug-in (see [vault_kv2_get lookup documentation](https://docs.ansible.com/ansible/devel/collections/community/hashi_vault/vault_kv2_get_lookup.html)) iterates over variables you want loaded from Vault. For each iteration it stores the iteration's output in `loop_var: "server"`. From that output we only really care about the `server.secret` dictionary. We append that to a `vault_data` dictionary which is first initialized as an empty dictionary and then expanded per iteration. When done `vault_data` contains key-values pair for all Vault variables. + +The next step can be this `role_include_vault-check` to hard-fail in case a key turned out to have an empty value. + +``` +- name: "If a secret is missing: Fail progress" + import_role: + name: "role_include_vault-check" + vars: + - vault_check_base_path: "{{ vault_check_base_path }}" + - vault_check_inc_vault_data: "{{ vault_data }}" + - vault_check_fail_check: + - "password" + - "password_salt" +``` + +## Output diff --git a/tasks/40-check-vault-var.yml b/tasks/40-check-vault-var.yml new file mode 100644 index 0000000..6cd11b8 --- /dev/null +++ b/tasks/40-check-vault-var.yml @@ -0,0 +1,7 @@ +- name: 'If a secret is missing: Fail progress' + failed_when: inc_fail_check not in inc_vault_data + loop_control: + loop_var: 'inc_fail_check' + loop: '{{ fail_check }}' + debug: + msg: 'Vault has {% if inc_fail_check not in inc_vault_data %}no {% endif %}secret ''{{ inc_fail_check }}'' at ''{{ vault_base_path }}'''