Performing an Ansible lookup using first_found and skip - ansible

I'd like to perform a lookup in Ansible using first_found as well as its skip option. I've created the following play to do so:
- name: Include group playbooks
include: "{{lookup('first_found', dict=(files=[item + '.yml', 'empty.yml'], skip=true))}}"
with_items: "{{group_names}}"
I'm receiving this error, however:
ERROR! Unexpected Exception: '_raw_params'
How can I pass skip option?

First of all, I doubt your dict= parameters passing. See my other answer for correct call.
And about your error: first_found lookup with skip option returns empty list if nothing is found – but include statement expects filename as free-form parameter.
You can workaround it like this:
- name: Include group playbooks
include: "{{ filename }}"
when: filename is string
vars:
filename: "{{ lookup('first_found', dict(files=[item + '.yml', 'empty.yml'], skip=true)) }}"
with_items: "{{group_names}}"

Related

How can I put the discovered values into loop variables so that they are on one line

How can I put the discovered values into loop variables so that they are on one line using Ansible task? I have now task like this
- name: Updating test.conf
lineinfile:
path: "/root/test.conf"
regexp: "test="
line: "test={{ hostvars[item]['ansible_env'].SSH_CONNECTION.split(' ')[2] }}"
state: present
with_nested:
- "{{groups['app']}}"
It needs that when invoking the job, it takes the IP addresses from the servers that are in the app group and puts them on a single line. Currently, it performs such a substitution twice with which they are replaced and finally there is only one address in the test parameter.
I need format after task like this:
test=1.1.1.1, 2.2.2.2
While jinja2 is heavily inspired from python, its does not allow you to do all the same operations. To join a list your would have to do something like:
- debug:
msg: "{{ myvar | join(',') }}"
vars:
myvar:
- foo
- bar
When in doubt, always use a simple playwook with a debug task to validate your jinja code.

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) }}"

Use expression as filter argument in ansible

I want to search the local filesystem for files matching a glob pattern, and get the path relative to a parent directory. Following is the code
- name: check for local configuration files
debug:
var:
- "{{ item | relpath('{{ playbook_dir }}/templates/httpd/{{ vhost.user }}') }}"
with_fileglob: "{{ playbook_dir }}/templates/httpd/{{ vhost.user }}/*.j2"
The above code is not working since Ansible is unable to resolve the nested expression?
I am planning to pre-evaluate the expression into a variable using set_fact and pass it to relpath.
Is it possible to achieve the result without this extra variable declaration? Is it possible to use an expression as argument to a filter?
You can access variables by name inside jinja expressions:
"{{ item | relpath(playbook_dir+'/templates/httpd/'+vhost.user) }}"

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) }}"

how to use lookup('file') in ansible when the file might not exist?

I want to lookup the contents of a file on the ansible control node --
example:
- hosts: all
vars:
somevar: "{{ lookup('file', playbook_dir + '/some/path' + inventory_hostname) }}"
if the file does not exist I'd like the variable to be undefined or set to a default value. The lookup module throws an error however if the file doesn't exist. What's the right way to handle this error so that I can branch on the existence of somevar within my code?
It seems that since this original post and now, this functionality has been added to the lookup plugin.
For me it works, I'm on version 2.8.5:
https://docs.ansible.com/ansible/latest/plugins/lookup.html#using-lookup-plugins
New in version 2.6.
You can now control how errors behave in all lookup plugins by setting errors to ignore, ...
- hosts: all
vars:
somevar: "{{ lookup('file', playbook_dir + '/some/path' + inventory_hostname, errors='ignore') }}"
I ended up using local_action rather than lookup ...
- name: "Lookup saved ssh hostkey for this host"
local_action: shell cat {{playbook_dir}}/path/to/{{inventory_hostname}}.pubkey
ignore_errors: True

Resources