For inventory purpose due to some business limitation, i wrote a playbook that retrieve content of resolv.conf + ntp.conf + timesyncd.conf and then, write the retrieved content within a CSV file.
Globally, maybe it's not the best practice but i set a fact at every task for retrieving file content, then i format the datas as i would and finally, write them in a csv like data1;data2;data3;
I got some issues/questions there :
- if the file doesn't exists,Ansible task fails and doesn't go to the next task, i know this is by design. To prevent that, should it be a good solution to use failed_when/changed_when conditionals near retrieving tasks ?
- in the last task ("Write results to …") how to handle non-existing fact ? at the moment if 3 facts are existing, file is written. If 1/3 facts doesn't exists but others are , nothing is written.
Thanks a lot for your advices. Playbook is below
---
- name: sys-check_conf_ntp_dns_net
hosts: my_servers
remote_user: my_user
tasks:
# RESOLV.CONF
- name: Retrieve remote /etc/resolv.conf
ansible.builtin.slurp:
src: /etc/resolv.conf
register: resolv_conf
- name: Format resolv_conf_fact data
set_fact:
resolv_conf_fact: "{{ (resolv_conf['content'] | b64decode) | regex_findall('\\s*nameserver\\s*(.*)') }}"
# NTP.CONF
- name: Retrieve remote /etc/ntp.conf
ansible.builtin.slurp:
src: /etc/ntp.conf
register: ntp_conf
- name: Format ntp_conf_fact data
set_fact:
ntp_conf_fact: "{{ (ntp_conf['content'] | b64decode) | regex_findall('(\\nserver.*?)(\\n)') }}"
# TIMESYNCD.CONF
- name: Retrieve /etc/systemd/timesyncd.conf
ansible.builtin.slurp:
src: /etc/systemd/timesyncd.conf
register: timesyncd_conf
- name: Format timesyncd_conf_fact data
set_fact:
timesyncd_conf_fact: "{{ (timesyncd_conf['content'] | b64decode) | regex_search('(NTP=f.*)') }}"
- name: Write results to /tmp/sys-check_conf_ntp_dns_net.csv
lineinfile:
path: /tmp/sys-check_conf_ntp_dns_net.csv
line: "Hostname:{{inventory_hostname}};resolv.conf:{{ resolv_conf_fact }};ntp.conf:{{ ntp_conf_fact }};timesyncd.conf:{{ timesyncd_conf_fact }};"
create: yes
delegate_to: localhost
EDIT
I finally found a workaround, not sure if it's legal or not 😀
For every fact i set, i add a default value, then as it's filtered through the regex, it behaves like the fact is not empty and so seems to work at the end.
For example :
Setting a fact before :
ntp_conf_fact: "{{ (ntp_conf['content'] | b64decode) | regex_findall('(\\nserver.*?)(\\n)') }}"
Setting a fact after :
ntp_conf_fact: "{{ ((ntp_conf['content']|default([blabla])) | b64decode) | regex_findall('(\\nserver.*?)(\\n)') }}"
Can someone confirm if it sounds ok ? or if anyone got a different solution ?
Best Answer
using
default
Your approach with
default
is absolutely correct and the way to go.You don't need all the brackets you put, you can write it simply as:
{{ ntp_conf['content'] | default('') | b64decode | regex_findall('(\\nserver.*?)(\\n)') }}
slurp fails if file not exists
Since
slurp
fails if the file does not exist, the consequence is that the execution is interrupted withfailed
.Sample output:
To prevent this, you should add an
ignore_errors: true
to theslurp
tasks. This way, if an error occurs, it will be ignored and execution will continue. You handle this exception as described above with the use ofdefault
.Example for the NTP task:
using
set_fact
or in-taskvars
Reading data and storing it in a variable using
set_fact
works without problems and can be implemented this way.Note: If you store values in a variable via
set_fact
, they are available for all following tasks until the end of the runtime (as long as it is not overwritten) and can be used in any number of tasks. The same applies to the data, which were stored byregister:
.If you need the data stored by
set_fact
only once in the task "Write results", you can define the variables directly in the corresponding task without having to formulate separateset_fact
tasks.Note: The variables, which are defined in a task with
vars:
, are only valid and available within this one task, i.e. they are in the scope of the task.Your playbook can look like this:
If the data is needed only once, the use of
vars:
has the advantage that the execution of the whole playbook is accelerated, because the number of tasks is reduced.