I’m getting my ansible_become_pass from my Bitwarden vault successfully, but I want to re-use it across plays within the playbook. To do this, I wanted to set it like so, but I think I’m trying to fetch it incorrectly. I tried also to set delegate_facts: true but it did not work.
TASK [Gathering Facts] ************************************************************************************************************
fatal: [ansible]: FAILED! => {"msg": "The field 'become_pass' has an invalid value, which includes an undefined variable.. 'ansible.vars.hostvars.HostVarsVars object' has no attribute 'bw_data'"}
Trying to call the variable this way causes Ansible to complain that the variable is empty.
vars:
ansible_become_pass: "{{ bw_data }}"
like so:
fatal: [ansible]: FAILED! => {"msg": "The field 'become_pass' has an invalid value, which includes an undefined variable.. 'bw_data' is undefined"}
I had this working prior but I wanted to try delegating the play instead of running in a different host section so it’s a shorter file. Really just playing around to be honest. Working playbook below:
How do I call this variable correctly, or is there a better way to go about this? I’m fine using the second (working) method if that’s what I’m meant to do.
long:
Ansible has 2 variable scopes, play objects and hosts, play objects variables are inherited only by other play objects contained in the scope defined (think functions and embeded functions). Since plays are at the same level, not contained in other plays, you cannot pass variables from one play to the other.
Host scope is sustained through the run and accessible via the hostvars variable, so variables persist across plays within the same run, this is the proper way to ‘persist’ variables across plays.
Your first attempt is failing because while you delegate execution to localhost you do not delegate_facts to it, so the facts are applied to the ‘current host’ aka inventory_hostname, do that and both ways 'will work’TM.
When I am creating playbooks, I try to avoid using hostvars - I consider this an internal structure of ansible.
Here is my approach to do what you are trying to do:
---
- name: Playbook with all shared variables (does not do anything)
hosts: all
gather_facts: false
vars: &shared_playbook_variables
bitwarden_password: "{{ lookup('community.general.bitwarden', 'badmin - Lab User', field='password')[0] }}"
bitwarden_password_for_current_host: "{{ lookup('community.general.bitwarden', ansible_hostname, field='password')[0] }}"
ansible_become_pass: "{{ bitwarden_password }}"
tasks: []
- name: First playbook
hosts: all
gather_facts: false
vars:
<<: *shared_playbook_variables
tasks:
- name: Simple debug for static variable
ansible.builtin.debug:
var: ansible_become_pass
- name: Debug for variable that depends on the ansible hostname
ansible.builtin.debug:
var: bitwarden_password_for_current_host
- name: Second playbook
hosts: all
gather_facts: false
vars:
<<: *shared_playbook_variables
tasks:
- name: Simple debug for static variable
ansible.builtin.debug:
var: ansible_become_pass
- name: Debug for variable that depends on the ansible hostname
ansible.builtin.debug:
var: bitwarden_password_for_current_host
In playbook above there are two variables - one is static bitwarden_password - we get the password only once, another one bitwarden_password_for_current_host depends on host.
First static variable, will not change from one place to another, but ansible will anyway get it for the second (and third time) because of its lazy evaluations. But this is a good thing, because we can use this feature in “dynamic” variable bitwarden_password_for_current_host - there isansible_hostname magic variable that will be filled on each run with correct info. So for each host there will be correct call to lookup plugin (and correct password for the host).
So maybe I’m missing something else or have a typo I don’t see? I tried setting delegate_facts: true on the play where I get the password from Bitwarden but that doesn’t make it to the ansible_become_pass variable.
I print the expected value in the test print statement when I run it (cheated by commenting out the ansible_become_pass to test), but I get the same error as before when running the playbook.
fatal: [ansible]: FAILED! => {"msg": "The field 'become_pass' has an invalid value, which includes an undefined variable.. 'ansible.vars.hostvars.HostVarsVars object' has no attribute 'bw_data'"}
There are of course different definitions of what scope is.
Don’t you thing that example above demonstrates that there is also a “file” scope in ansible (or yaml)?
Hi kks, would each playbook be a separate .yaml file and I would call them when I run everything, or is this all in one file? I’ll give that method a go too, I like the idea of using ansible hosts for a variable when searching for a password.
no, there is no ‘file’ scope, as you can have multiple files per run ansible-playbook play1.yml play2.yml the host scope is ‘per run’ as i mentioned before, the files are not relevant for scope as much as the contained playbook objects are, the scope would be the same in the run if the plays were defined in the same file or in different files.
What you are doing with YAML is pre generating a copy of the variables with YAML anchors, this is transparent to Ansible itself, this is not keeping variables in ‘file scope’ but using a property of YAML to make several copies of the same variables.
Similarly this can be archived with inventory files, just set variables you need for group “all”. Inventory can span across playbook runs.
Do not worry to set variables to something that seems undefined, ansible will only read and evaluate variables when they are needed. Basically everywhere you can add variable:
this_variable_is_never_used: put here whatevery, just valid yaml syntax, this will not have any influence for playbook run