How to init var of hostvars to null - ansible

I would like to reset a varaible to an empty list on all nodes of my inventory, whether it's already existing or not.
The best I come with is:
- name: clear union_files list
set_fact:
hostvars: "{{ hostvars.item | combine({'union_files': []}) }}"
loop: "{{ groups['all'] }}"
run_once: true
But with this code, I've got this error that I don't understand:
MSG:
|combine expects dictionaries, got Undefined
Why it is saying undefined while I'm setting the dict {'union_files': []}
Have you got any clue? What would by the most ansible way to do so?

There are a couple of things going on with this; first, hostvars.item is not the same as hostvars[item]; the former syntax is designed for a quick-and-dirty way of dereferencing keys in a dict, such as ansible_version.full which is not a variable named full, it's ansible_version["full"]. The latter is looking up a variable key name in the dict
The second thing going on is that you are attempting to assign hostvars as if it were a fact, but it's not a fact, it's a namespace in which facts live. So even if you were to do hostvars: {{ hostvars[item] }} it would create a fact named hostvars in the hostvars namespace
If you just want to clear the union_files fact, then use set_fact as you normally would:
- name: clear union_files list
set_fact:
union_files: []
and, assuming the playbook in which that task lives is hosts: all, then it will cheerfully set, or reset, union_files to [] on every host

Thanks Matthew,
I finally come up with this solution:
- name: clear list
set_fact:
union_files: []
delegate_to: item
loop: "{{ ansible_play_hosts }}"
run_once: true
which do the job.

Related

Access HostVar in Ansible

I'm new to Ansible, I'm trying to use the 'Name' fact that is stored in the hostvars of ansible but, I keep getting variable not set.
I have pumped out all the host vars to confirm it is there, using the following task in my play:
- name: Print all variables for each remote device
debug:
var: hostvars[inventory_hostname]
run_once: true
delegate_to: "{{ groups['dir_tier'] | first }}"
I can see the fact is there, truncated output of hostvars:
{"hostvars[inventory_hostname]":
"{'tld':'{{environment_name}}.{{mgmt_domain}}',
'download_dir':'/tmp',
'archive_file_server':
'{{
file_server
}}',
'transport':
'repositories',
'java_package':
'jdk',
'Name':
'idserver99'
}
Have tried several ways to target the 'Name' but keep coming back to the not defined issue.
hostvars[inventory_hostname][Name]
hostvars[inventory_hostname].[Name]
{{ hostvars[inventory_hostname].[Name] }}
{{ hostvars[inventory_hostname][Name] }}
I dont seem to be able to crack the correct syntax pointer welcome.
Keen to learn.

Ansible variable value as variable name

I want to make a variable named as value of _id.
And it seems to be working fine (tested on _id value i know), except I have no clue how to deal with it via _id field.
- name: Set fact
set_fact:
"{{_id}}": "{{ _name }}"
You can do this with the set_fact module, you just have to change your syntax up a bit to reference the metadata in a dictionary for the variable.
- set_fact: {"{{ item.key }}":"{{ item.val }}"}
loop: "{{ item }}"
loop_control:
loop_var: item
Let me know if you have any questions or need any additional help. Happy to assist if I can. Ansible is fun. :)

Parse json data from module output in ansible [duplicate]

I'm trying to transform some fields of the items of a list in an Ansible Playbook. Here is the simplest reproduction path, skipping the transformation. The result should be identical to the users variable.
---
# Run with:
# ansible-playbook -i "localhost," loop3.yml
- hosts: localhost
connection: local
gather_facts: false
vars:
users:
- name: paul
uid: 1
- name: pete
uid: 2
tasks:
- set_fact:
args:
useritem:
name: '{{ item.name }}'
uid: '{{ item.uid }}'
with_items:
- users
register: sf_result
- debug: var=sf_result
- set_fact:
userslist: "{{ sf_result.results | map(attribute='ansible_facts.useritem') | list }}"
- debug: var=userslist
I get this error:
TASK [set_fact useritem={u'name': u'{{ item.name }}', u'uid': u'{{ item.uid }}'}] ***
fatal: [localhost]: FAILED! => {"failed": true, "msg": "ERROR! 'unicode object' has no attribute 'name'"}
There are several examples very close to what I needbut I could find no working example using set_fact along with with_items and items as a map.
I've tried Ansible 1.9.2, 1.9.4, and 2.0.0-0.6.rc1 with different error messages but no more success. Ansible 2 should allow skipping the second call to set_fact but the error happens before getting there.
I thought I did read somewhere that with_items accepts a bare variable name, but it's not the case.
The program runs as expected using:
with_items: "{{ users }}"
Referencing simple variables
After you define a variable, use Jinja2 syntax to reference it. Jinja2 variables use double curly braces. For example, the expression users goes to {{ users }} demonstrates the most basic form of variable substitution. You can use Jinja2 syntax in playbooks. For example:
with_items: "{{ users }}"
and also
loop: "{{ users }}"
Now, the following parameters can be used to loop through an array/dictionary/list.
loop (preferred)
with_items
with_list
NOTE: When possible, Ansible recommends using the loop parameter, as the loop parameter is meant to supersede the with_items option.
with_items:
Ansible with_items is a lookup type plugin that is used to return list items passed into it. When we pass a list of items to a task, then the task will be performed for all items in that list. If a high-level item has also another list, then that list will be flattened and Ansible will not perform recursion for it. This feature is not available in it. Because that is done by another plugin named list lookup. You can use it to achieve recursion.
Also, you can pass multiple entries in a single item to pass data to parameters when you are running a task that needs more than one parameter like while adding a user, you might need to pass userid, name, groups, etc. This flexibility makes it more suitable in real-world scenarios.
- name: with_items
ansible.builtin.debug:
msg: "{{ item }}"
with_items: "{{ items }}"
- name: with_items -> loop
ansible.builtin.debug:
msg: "{{ item }}"
loop: "{{ items|flatten(levels=1) }}"
Comparing loop and with_*
The with_ keywords rely on Lookup plugins - even items is a lookup.
The loop keyword is equivalent to with_list and is the best choice for simple loops.
The loop keyword will not accept a string as input, see Ensuring list input for loop: using query rather than lookup.
Generally speaking, any use of with_* covered in Migrating from with_X to loop can be updated to use loop.
Be careful when changing with_items to loop, as with_items performed implicit single-level flattening. You may need to use flatten(1) with loop to match the exact outcome. For example, to get the same output as:
with_items:
- 1
- [2,3]
- 4
you would need
loop: "{{ [1, [2, 3], 4] | flatten(1) }}"
Any with_* statement that requires using lookup within a loop should not be converted to use the loop keyword. For example, instead of doing:
loop: "{{ lookup('fileglob', '*.txt', wantlist=True) }}"

Overwrite ansible variable from file fails

I'm trying to overwrite a previously defined variable my_var (when it's set to LATEST) by loading a value from a file (containing, say, NEWVALUE).
- name: Load from file
vars:
my_var: "{{ lookup('file', '~/file.txt') }}"
my_var2: "{{ lookup('file', '~/file.txt') }}"
debug: msg="my_var is {{ my_var }} my_var2 is {{ my_var2 }}"
when: "{{ my_var=='LATEST' }}"
This prints
ok: [host] ==> {
"msg": "my_var is LATEST my_var2 is NEWVALUE"
}
So I feel that I've verified that I'm loading the value correctly.. but for some reason I can't set the result of lookup in a previously set variable. Disabling the when clause doesn't seem to make any difference.
Should I be able to do this? As an alternative I'm going to use a third variable and just set it to either the preexisting value or the value from the file - but this seems like an unnecessary step to me.
Ansible version 2.1.0.0 b.t.w.
The vars you defined in your example only are available for the single debug task.
As you mentioned in the comment you figured this out and used set_fact instead. And yes, this won't work if you have passed the same variable as extra-var, as it has the highest precedence. There is no way to override a variable you passed as extra-var.

How to use Ansible's with_item with a variable?

I'm trying to transform some fields of the items of a list in an Ansible Playbook. Here is the simplest reproduction path, skipping the transformation. The result should be identical to the users variable.
---
# Run with:
# ansible-playbook -i "localhost," loop3.yml
- hosts: localhost
connection: local
gather_facts: false
vars:
users:
- name: paul
uid: 1
- name: pete
uid: 2
tasks:
- set_fact:
args:
useritem:
name: '{{ item.name }}'
uid: '{{ item.uid }}'
with_items:
- users
register: sf_result
- debug: var=sf_result
- set_fact:
userslist: "{{ sf_result.results | map(attribute='ansible_facts.useritem') | list }}"
- debug: var=userslist
I get this error:
TASK [set_fact useritem={u'name': u'{{ item.name }}', u'uid': u'{{ item.uid }}'}] ***
fatal: [localhost]: FAILED! => {"failed": true, "msg": "ERROR! 'unicode object' has no attribute 'name'"}
There are several examples very close to what I needbut I could find no working example using set_fact along with with_items and items as a map.
I've tried Ansible 1.9.2, 1.9.4, and 2.0.0-0.6.rc1 with different error messages but no more success. Ansible 2 should allow skipping the second call to set_fact but the error happens before getting there.
I thought I did read somewhere that with_items accepts a bare variable name, but it's not the case.
The program runs as expected using:
with_items: "{{ users }}"
Referencing simple variables
After you define a variable, use Jinja2 syntax to reference it. Jinja2 variables use double curly braces. For example, the expression users goes to {{ users }} demonstrates the most basic form of variable substitution. You can use Jinja2 syntax in playbooks. For example:
with_items: "{{ users }}"
and also
loop: "{{ users }}"
Now, the following parameters can be used to loop through an array/dictionary/list.
loop (preferred)
with_items
with_list
NOTE: When possible, Ansible recommends using the loop parameter, as the loop parameter is meant to supersede the with_items option.
with_items:
Ansible with_items is a lookup type plugin that is used to return list items passed into it. When we pass a list of items to a task, then the task will be performed for all items in that list. If a high-level item has also another list, then that list will be flattened and Ansible will not perform recursion for it. This feature is not available in it. Because that is done by another plugin named list lookup. You can use it to achieve recursion.
Also, you can pass multiple entries in a single item to pass data to parameters when you are running a task that needs more than one parameter like while adding a user, you might need to pass userid, name, groups, etc. This flexibility makes it more suitable in real-world scenarios.
- name: with_items
ansible.builtin.debug:
msg: "{{ item }}"
with_items: "{{ items }}"
- name: with_items -> loop
ansible.builtin.debug:
msg: "{{ item }}"
loop: "{{ items|flatten(levels=1) }}"
Comparing loop and with_*
The with_ keywords rely on Lookup plugins - even items is a lookup.
The loop keyword is equivalent to with_list and is the best choice for simple loops.
The loop keyword will not accept a string as input, see Ensuring list input for loop: using query rather than lookup.
Generally speaking, any use of with_* covered in Migrating from with_X to loop can be updated to use loop.
Be careful when changing with_items to loop, as with_items performed implicit single-level flattening. You may need to use flatten(1) with loop to match the exact outcome. For example, to get the same output as:
with_items:
- 1
- [2,3]
- 4
you would need
loop: "{{ [1, [2, 3], 4] | flatten(1) }}"
Any with_* statement that requires using lookup within a loop should not be converted to use the loop keyword. For example, instead of doing:
loop: "{{ lookup('fileglob', '*.txt', wantlist=True) }}"

Resources