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 with failed
.
Sample output:
TASK [Retrieve remote /etc/resolv.conf] ************************************************************
ok: [server1]
TASK [Format resolv_conf_fact data] ****************************************************************
ok: [server1]
TASK [Retrieve remote /etc/ntp.conf] ***************************************************************
fatal: [server1]: FAILED! => {"changed": false, "msg": "file not found: /etc/ntp.conf"}
PLAY RECAP *****************************************************************************************
server1 : ok=2 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
To prevent this, you should add an ignore_errors: true
to the slurp
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 of default
.
Example for the NTP task:
- name: Retrieve remote /etc/ntp.conf
ansible.builtin.slurp:
src: /etc/ntp.conf
ignore_errors: true
register: ntp_conf
using set_fact
or in-task vars
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 by register:
.
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 separate set_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:
---
- 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
ignore_errors: true
register: resolv_conf
# NTP.CONF
- name: Retrieve remote /etc/ntp.conf
ansible.builtin.slurp:
src: /etc/ntp.conf
ignore_errors: true
register: ntp_conf
# TIMESYNCD.CONF
- name: Retrieve /etc/systemd/timesyncd.conf
ansible.builtin.slurp:
src: /etc/systemd/timesyncd.conf
ignore_errors: true
register: timesyncd_conf
- 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 }};ntp.conf:{{ ntp }};timesyncd.conf:{{ timesyncd }};"
create: yes
vars:
resolv: "{{ resolv_conf['content'] | default('') | b64decode | regex_findall('\\s*nameserver\\s*(.*)') }}"
ntp: "{{ ntp_conf['content'] | default('') | b64decode | regex_findall('(\\nserver.*?)(\\n)') }}"
timesyncd: "{{ timesyncd_conf['content'] | default('') | b64decode | regex_search('(NTP=f.*)') }}"
delegate_to: localhost
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.