Ansible task vars override set_fact vars - ansible

In one of my ansible playbook, i set a variable for all the playbook by doing :
set_fact:
domain_path: "{{ X.X.domain_path }}"
During the playbook, one of my tasks uses a role, that needs a domain_path variable that is different than the one i defined in the beginning
include_role:
name: role_X
vars:
domain_path: "/a/different/path"
When i try to use domain_path, in another bloc after the role, it doesn't give me the set_fact value but the tasks value, which i do not want.
I can give more details if needed

Q: "task vars override set_fact vars"
A: task vars (precedence 17) can't override set_facts (precedence 19) for the rest of the play. See Variable precedence: Where should I put a variable?. Review the role. Very probably role (and include_role) params (precedence 20) override the variable.
I can't reproduce the problem. I created a fresh role with single task only
shell> cat roles/role_X/tasks/main.yml
- debug:
var: domain_path
The playbook
shell> cat pb.yml
- hosts: localhost
tasks:
- set_fact:
domain_path: "X.X.domain_path"
- debug:
var: domain_path
- include_role:
name: role_X
vars:
domain_path: "/a/different/path"
- debug:
var: domain_path
gives as expected
shell> ansible-playbook pb.yml | grep domain_path
domain_path: X.X.domain_path
domain_path: /a/different/path
domain_path: X.X.domain_path

It is not good practice to use the same variable name for different use cases. You should change the name of one (or both) of your variables so they do not conflict.
If there is a need, you can always use one value to set the other if that is your goal at some point in the task.

Related

Use value of CLI variable as the name of a host_vars variable?

Is there a way to use the value of one Ansible variable as the name of another variable so I can extract a value from its list?
host_vars:
this:
does: walk
says: hi
that:
does: run
says: hello
On the CLI when I run the playbook, I add -e="thing=this".
In the playbook, I've tried all manner of things to expand the variable thing to its value this, then use this to extract the value of does in the host_vars file.
Using the variable name directly obviously works:
- name: Check what the thing does
debug:
msg: "{{ this['does'] }}"
But the following do not:
{{ thing['does'] }}
{{ {{ thing }}['does'] }}
Those, plus several other iterations I've tried all either throw an error or print out the literal string.
You need the vars lookup plugin to address variables indirectly. See
shell> ansible-doc -t lookup vars
For example,
- debug:
msg: "{{ lookup('vars', thing).does }}"
should give (abridged)
shell> ansible-playbook pb.yml -e "thing=this"
...
msg: walk
Example of a complete playbook for testing
- hosts: localhost
vars:
this:
does: walk
says: hi
that:
does: run
says: hello
tasks:
- debug:
msg: "{{ lookup('vars', thing).does }}"
You can simplify the code further by putting all declarations into the vars. For example, into the group_vars/all
shell> cat group_vars/all/thing.yml
this:
does: walk
says: hi
that:
does: run
says: hello
_thing: "{{ lookup('vars', thing) }}"
Then, the simplified playbook below gives the same results
- hosts: localhost
tasks:
- debug:
var: _thing.does

How can I specify hosts when importing a playbook

ansible version: 2.9
Hi.
How can I specify the hosts when I import a playbook with import_playbook?
My code (/project/first_pb.yml)
- import_playbook: /test/pb0.yml
hosts: atlanta
Q: "A method to pass specific family hosts to the imported playbook?"
A: There is no difference between a playbook imported or not. For example,
shell> cat pb-A.yml
- hosts: "{{ my_hosts|default('localhost') }}"
tasks:
- debug:
var: inventory_hostname
shell> ansible-playbook pb-A.yml -e my_hosts=host1
...
inventory_hostname: host1
shell> cat pb-B.yml
- import_playbook: pb-A.yml
shell> ansible-playbook pb-B.yml -e my_hosts=host1
...
inventory_hostname: host1
There are many options on how to pass specific hosts and groups to a playbook. For example, see:
Patterns: targeting hosts and groups
add_host module – Add a host and group to the ansible-playbook in-memory inventory
Inventory plugins (e.g. constructed)
I can filter play plabooks with "when: " condition, for example:
- import_playbook: /test/pb0.yml
when: hostname != host1a*
- import_playbook: /test/pb0.yml
when: '"north" not in hostname'
- import_playbook: /test/pb0.yml
when: '"west" in hostname'

Ansible - dynamic variable with host_vars and string in the name

I'm trying to create a job role in Ansible to run yum install/update of packages, which will be provided by a 3rd party system as a .yml file to vars directory in a role with following convention: server01.yml, server02.yml, serverX.yml with variable in form packageList_serverNumber: 'list of packages'.
This variable will be read using a task:
- name: server update packages from host_vars
yum:
name: "{{ install_pkgs }}"
state: latest
This should point to host_vars file for specific host:
install_pkgs: "{{ packageList_server01 }}"
As this task should only run when the variable is defined, I was trying to use when clause with variable which will point to packageList_serverNumber. When I hardcode it, like below it is working:
when: packageList_server01 is defined
Can you please advise how to make it dynamic?
I was trying with:
when: packageList_{{hostvars[inventory_hostname]}} is defined
But unfortunately this is not working.
Use lookup plugin vars. Run the command below to see the details
shell> ansible-doc -t lookup vars
Given the vars files
shell> cat roles/test4/vars/server01.yml
packageList_server01: [pkg1, pkg2, pkg3]
shell> cat roles/test4/vars/server02.yml
packageList_server02: [pkg4, pkg5, pkg6]
shell> cat roles/test4/vars/server03.yml
packageList_server03: [pkg7, pkg8, pkg9]
read the vars, declare the variable install_pkgs, and use it
shell> cat roles/test4/tasks/main.yml
- include_vars: "vars/{{ inventory_hostname }}.yml"
- set_fact:
install_pkgs: "{{ lookup('vars', 'packageList_' ~ inventory_hostname) }}"
- debug:
msg: "Install {{ install_pkgs }}"
For example the playbook
- hosts: server01,server02,server03
gather_facts: false
roles:
- test4
gives (abridged)
TASK [test4 : debug] ****
ok: [server01] =>
msg: Install ['pkg1', 'pkg2', 'pkg3']
ok: [server03] =>
msg: Install ['pkg7', 'pkg8', 'pkg9']
ok: [server02] =>
msg: Install ['pkg4', 'pkg5', 'pkg6']

Ansible: print only user defined variables

How can I print variables declared only at group_vars, host_vars without ansible facts?
such code is good:
- name: "Ansible | List all known variables and facts"
debug:
var: hostvars[inventory_hostname]
But I don't need host IPs,disks, etc.
I mean to check all my variables one more time before continue to execute Play.
There are 3 categories of variables: ansible facts, special variables, and user's variables. Remove both ansible facts and special variables from hostvars and what is left are user's variables. The list of the ansible facts is available in the variables ansible_facts. The list of the special variables must be created (I think).
Create a list of special variables
If you run the playbook below you'll see the list of the special variables and user's variables
- hosts: localhost
tasks:
- debug:
msg: "{{ hostvars[inventory_hostname]|
difference(ansible_facts) }}"
Eliminate the user's vars and put the list of the special variables into a file. For example
shell> cat special_vars.yml
special_vars:
- ansible_python_interpreter
- ansible_connection
- inventory_hostname
...
This list of special variables might be not complete and will serve the purpose of this host only.
Remove ansible facts and special variables from hostvars
- hosts: localhost
vars_files:
- special_vars.yml
tasks:
- set_fact:
user_var1: AAA
- debug:
msg: "{{ hostvars[inventory_hostname]|
difference(ansible_facts)
difference(special_vars) }}"
gives the list of user's variables only
msg:
- user_var1
The user's variables will include also the configuration variables set by the user (e.g. connection variables: ansible_user or priviledge escalation: ansible_become).
Name-space
A better practice is to "name-space" variables. For example
- hosts: localhost
vars:
prj51_var1: AAA
prj51_var2: BBB
tasks:
- debug:
msg: "{{ item }}: {{ query('vars', item)|first }}"
loop: "{{ query('varnames', 'prj51_.+$') }}"
gives
msg: 'prj51_var1: AAA'
msg: 'prj51_var2: BBB'

Passing a variable to an included Ansible playbook

I'd like to pass a variable to an included Ansible playbook as follows:
---
- hosts: localhost
connection: local
vars:
my_group: foo
- include: site.yml hosts={{ my_group }}
Then, in site.yml...
---
- hosts: "{{ hosts }}"
...
Unfortunately, I get an error saying that my_group is undefined in site.yml. Ansible docs do say that:
Note that you cannot do variable substitution when including one playbook inside another.
Is this my case? Is there a way around it?
You can use this syntax, but my_group has to be defined at the global level. Now it's local to the first play - it's even clear from the indentation.
You can confirm this by running your playbook with --extra-vars my_group=foo.
But generally what you seem to want to achieve is done using in-memory inventory files and add_host module. Take this as an example:
- hosts: localhost
gather_facts: no
vars:
target_host: foo
some_other_variable: bar
tasks:
- add_host:
name: "{{ target_host }}"
groups: dynamically_created_hosts
some_other_variable: "{{ some_other_variable }}"
- include: site.yml
with site.yml:
---
- hosts: dynamically_created_hosts
tasks:
- debug:
var: some_other_variable
I added some_other_variable to answer the question from your comment "how do I make a variable globally available from inside a play". It's not global, but it's passed to another play as a "hostvar".
From what I see (and I can't explain why) in Ansible >=2.1.1.0, there must be an inventory file specified for the dynamic in-memory inventory to work. In older versions it worked with Ansible executed ad hoc, without an inventory file, but now you must run ansible-playbook with -i inventory_file or have an inventory file defined through ansible.cfg.

Resources