Using Ansible "find" to find files in directory that match - ansible

I am trying to use "find" to find all files in a directory with certain filenames, but it doesn't seem to be working:
- hosts: all
tasks:
- name: "Ansible | Print a variable"
debug:
msg: "The play_tomcat_dest_dir is {{ play_tomcat_dest_dir }}"
- name: Delete any mysql-connector-java-*.jar files
find:
paths: "{{ play_tomcat_dest_dir }}lib"
patterns: "mysql-connector-java-*.jar"
use_regex: true
register: wildcard_files_to_delete
- name: "Ansible | Print a variable"
debug:
msg: "The wildcard_files_to_delete is {{ wildcard_files_to_delete }}"
The play_tomcat_dest_dir is:
/apps/apache-tomcat-9.0.54/
and "/apps/apache-tomcat-9.0.54/lib" directory has at least one "mysql-connector-java-...jar" file.
But when I run this playbook (using ansible-playbook) I am getting:
PLAY [all] **********************************************************************************************************************************************************************
TASK [Gathering Facts] **********************************************************************************************************************************************************
ok: [test_host]
TASK [Ansible | Print a variable] ***********************************************************************************************************************************************
ok: [test_host] => {
"msg": "The play_tomcat_dest_dir is /apps/apache-tomcat-9.0.54/"
}
TASK [Delete any mysql-connector-java-*.jar files] ******************************************************************************************************************************
ok: [test_host]
TASK [Ansible | Print a variable] ***********************************************************************************************************************************************
ok: [test_host] => {
"msg": "The wildcard_files_to_delete is {u'files': [], u'changed': False, 'failed': False, u'examined': 55, u'msg': u'', u'matched': 0}"
}
PLAY RECAP **********************************************************************************************************************************************************************
test_host : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Can someone tell me what is wrong with the "find"?
Thanks! Jim

I found the answer:
https://devops.stackexchange.com/questions/5070/how-do-i-get-the-find-module-to-work-in-ansible
I commented out the "use_regex" line, and then it worked :)!!

Related

Using ansible variable inside gathered fact list

I'm stuck to get data from gathered fact, using calculated data as part of query.
I am using 2.9 ansible and here is my task
---
- hosts: ios
connection: network_cli
gather_facts: true
tasks:
- name: CEF OUTPUT
ios_command:
commands: sh ip cef 0.0.0.0 0.0.0.0 | i nexthop
register: cef
- set_fact:
reg_result: "{{ cef.stdout |string| regex_search('Tunnel[0-9]+')}}"
- name: IT WORKS!
debug:
msg: "{{ reg_result }}"
- name: MANUAL LIST
debug:
var: ansible_facts.net_interfaces.Tunnel3.description
- name: AUTO LIST
debug:
var: ansible_facts.net_interfaces.[reg_result].description
and here is output
PLAY [ios] **********************************************
TASK [Gathering Facts] **********************************
ok: [10.77.3.1]
TASK [CEF OUTPUT] ***************************************
ok: [10.77.3.1]
TASK [set_fact] *****************************************
ok: [10.77.3.1]
TASK [IT WORKS!] ****************************************
ok: [10.77.3.1] => {
"msg": "Tunnel3"
}
TASK [MANUAL LIST] **************************************
ok: [10.77.3.1] => {
"ansible_facts.net_interfaces.Tunnel3.description": "DMVPN via MTS"
}
TASK [AUTO LIST] ****************************************
fatal: [10.77.3.1]: FAILED! => {"msg": "template error while templating string: expected name or number. String: {{ansible_facts.net_interfaces.[reg_result].description}}"}
to retry, use: --limit #/home/user/ansible/retry/ios_find_gw_int.retry
PLAY RECAP **********************************************
10.77.3.1 : ok=5 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
You see. Now I know that my default gateway is pointing to "Tunnel3", and it is possible to get some data placing this "Tunnel3" in {{ ansible_facts.net_interfaces.Tunnel3.description }} but how to get this automatically? And I feel such nested variable in the list is a very handy tool.
Remove the dot if you use the indirect addressing
- name: AUTO LIST
debug:
var: ansible_facts.net_interfaces[reg_result].description
See Referencing key:value dictionary variables.

How to skip a template copy in Ansible if the file has a pattern?

Trying to only copy an Nginx config file if the destination file does not have a string in it.
I thought this would work:
- name: Copy nginx config file
template:
src: templates/nginx.conf
dest: /etc/nginx/sites-enabled/default
validate: grep -l 'managed by Certbot' %s
But this task fails if "managed by Certbot" isn't in the file and stops the playbook run.
How can I just skip the template copy if the destination file already has that pattern? Maybe there's a better way to get the same result?
Inspired from this other answer
You can check for the presence of a content in a file using the lineinfile module in check mode. Then you can use the result as a condition to your template task. The default in the condition is to cope with the case when the file does not exists and the found attribute is not in the registered result.
---
- name: Check for presence of "managed by Certbot" in file
lineinfile:
path: /etc/nginx/sites-enabled/default
regexp: ".*# managed by Certbot.*"
state: absent
check_mode: yes
changed_when: false
register: certbot_managed
- name: Copy nginx config file when not certbot managed
template:
src: templates/nginx.conf
dest: /etc/nginx/sites-enabled/default
when: certbot_managed.found | default(0) == 0
You could use the failed_when condition and base it on the fail message that validate generate — failed to validate — to act upon:
- name: Copy nginx config file
template:
src: templates/nginx.conf
dest: /etc/nginx/sites-enabled/default
validate: grep -l 'managed by Certbot' %s
failed_when:
- copy_config_file.failed
- copy_config_file.msg != 'failed to validate'
register: copy_config_file
Note: in when and *_when, having a list of conditions is like doing list.0 and list.1 and ...
Given the playbook:
- hosts: all
gather_facts: no
tasks:
- copy:
dest: templates/nginx.conf
content: "{{ content | default('some random content') }}"
- copy:
dest: /etc/nginx/sites-enabled/default
content: "blank"
- template:
src: templates/nginx.conf
dest: /etc/nginx/sites-enabled/default
validate: grep -l 'managed by Certbot' %s
failed_when:
- copy_config_file.failed
- copy_config_file.msg != 'failed to validate'
register: copy_config_file
- shell: cat templates/nginx.conf
register: template_content
failed_when: false
- shell: cat /etc/nginx/sites-enabled/default
register: file_content
failed_when: false
- debug:
var: template_content.stdout
- debug:
var: file_content.stdout
When run via:
ansible-playbook play.yml
It gives:
PLAY [all] *******************************************************************************************************
TASK [copy] ******************************************************************************************************
changed: [localhost]
TASK [copy] ******************************************************************************************************
changed: [localhost]
TASK [template] **************************************************************************************************
ok: [localhost]
TASK [shell] *****************************************************************************************************
changed: [localhost]
TASK [shell] *****************************************************************************************************
changed: [localhost]
TASK [debug] *****************************************************************************************************
ok: [localhost] => {
"template_content.stdout": "some random content"
}
TASK [debug] *****************************************************************************************************
ok: [localhost] => {
"file_content.stdout": "blank"
}
PLAY RECAP *******************************************************************************************************
localhost : ok=7 changed=4 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Now, when run with
ansible-playbook play.yml -e "content='managed by Certbot\nsome other content'"
With an extra parameter to modify the content of the template, it gives:
PLAY [all] *******************************************************************************************************
TASK [copy] ******************************************************************************************************
ok: [localhost]
TASK [copy] ******************************************************************************************************
changed: [localhost]
TASK [template] **************************************************************************************************
changed: [localhost]
TASK [shell] *****************************************************************************************************
changed: [localhost]
TASK [shell] *****************************************************************************************************
changed: [localhost]
TASK [debug] *****************************************************************************************************
ok: [localhost] => {
"template_content.stdout": "managed by Certbot\nsome other content"
}
TASK [debug] *****************************************************************************************************
ok: [localhost] => {
"file_content.stdout": "managed by Certbot\nsome other content"
}
PLAY RECAP *******************************************************************************************************
localhost : ok=7 changed=4 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Register command output as 2 separate variables

I have an Ansible playbook that runs a script over my remote machines.
The script finds log files, determine the application the log file came from (so it would know the destination directory), and uploads the files back to the Ansible server (using scp).
Just because it worth mentioning - there are 2 different playbooks, one for Windows servers, using a PowerShell script, and the other is for Linux servers, using a bash script.
I would like to eliminate the scp part for those scripts, and instead using the fetch module in my playbooks. But I'm facing a problem with that - my scripts generates 2 variables for each application on each server:
the log file, full path.
the destination directory in the Ansible server (determined by the servers name and the application name).
My current playbook looks like that (this is the Windows one):
---
- hosts: <WindowsHosts>
gather_facts: no
tasks:
- name: Find and Copy Windows Applications Logs
script: fetchwinFindLogs.ps1 {{ server_partition }} {{days}} {{ansibleSRV_Port}} {{ansibleSRV_IP}}
register: result
- debug:
var: result
# - fetch: src={{ result }} dest={{ result }}
# with_items: "{{ result.stdout_lines }}"
The output:
PLAY [<WinHost>] *********************************************************************************************************************************************************************************************
TASK [Find and Copy Windows Applications Logs] ***************************************************************************************************************************************************************
changed: [<WinHost>]
TASK [debug] *************************************************************************************************************************************************************************************************
ok: [<WinHost>] => {
"result": {
"changed": true,
"failed": false,
"rc": 0,
"stderr": "",
"stderr_lines": [],
"stdout": "C:\\<Maindir>\\<App1>\\Logs\\platform.log.2020-09-18\r\n/<LinuxMainDir>/<logs>/Applications/<serverName>.<App1>/\r\nC:\\<Maindir>\\<App2>\\Logs\\platform.log.2020-09-18\r\n/<LinuxMainDir>/<logs>/Applications/<serverName>.<App2>/\r\nC:\\<Maindir>\\<App3>\\Logs\\platform.log.2020-09-18\r\n/<LinuxMainDir>/<logs>/Applications/<serverName>.<App3>/\r\nC:\\<Maindir>\\<App4>\\Logs\\platform.log.2020-09-18\r\n/<LinuxMainDir>/<logs>/Applications/<serverName>.<App4>/\r\n",
"stdout_lines": [
"C:\\<Maindir>\\<App1>\\Logs\\platform.log.2020-09-18",
"/<LinuxMainDir>/<logs>/Applications/<serverName>.<App1>/",
"C:\\<Maindir>\\<App2>\\Logs\\platform.log.2020-09-18",
"/<LinuxMainDir>/<logs>/Applications/<serverName>.<App2>/",
"C:\\<Maindir>\\<App3>\\Logs\\platform.log.2020-09-18",
"/<LinuxMainDir>/<logs>/Applications/<serverName>.<App3>/",
"C:\\<Maindir>\\<App4>\\Logs\\platform.log.2020-09-18",
"/<LinuxMainDir>/<logs>/Applications/<serverName>.<App4>/"
]
}
}
PLAY RECAP ***************************************************************************************************************************************************************************************************
<WinHost> : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
How do I use the 2 separate values gotten from the script, so the fetch module would know to direct each log file from each application to the correct directory in my Ansible server?
This includes, of course, looping over every 2 values, so it would direct the correct log to the correct destination directory (since, again, the first value will always be the full path log, and the second would be the destination directory, for each application separately).
There is possibly plenty of ways to achieve this.
Here are two of them:
Using the extended loop variables and a when condition, you could skip every other item with a when and a modulo % and get the item along with the ansible_loop.nextitem.
Here is an example for that:
- hosts: all
gather_facts: no
tasks:
- debug:
msg:
- "src: {{ item  }}"
- "dest: {{ ansible_loop.nextitem }}"
loop: "{{ result.stdout_lines }}"
when: ansible_loop.index % 2 == 1
loop_control:
extended: yes
vars:
result:
stdout_lines:
- "C:\\<Maindir>\\<App1>\\Logs\\platform.log.2020-09-18"
- "/<LinuxMainDir>/<logs>/Applications/<serverName>.<App1>/"
- "C:\\<Maindir>\\<App2>\\Logs\\platform.log.2020-09-18"
- "/<LinuxMainDir>/<logs>/Applications/<serverName>.<App2>/"
- "C:\\<Maindir>\\<App3>\\Logs\\platform.log.2020-09-18"
- "/<LinuxMainDir>/<logs>/Applications/<serverName>.<App3>/"
- "C:\\<Maindir>\\<App4>\\Logs\\platform.log.2020-09-18"
- "/<LinuxMainDir>/<logs>/Applications/<serverName>.<App4>/"
This yields the recap:
PLAY [all] ********************************************************************************************************
TASK [debug] ******************************************************************************************************
ok: [localhost] => (item=C:\<Maindir>\<App1>\Logs\platform.log.2020-09-18) => {
"msg": [
"src: C:\\<Maindir>\\<App1>\\Logs\\platform.log.2020-09-18",
"dest: /<LinuxMainDir>/<logs>/Applications/<serverName>.<App1>/"
]
}
skipping: [localhost] => (item=/<LinuxMainDir>/<logs>/Applications/<serverName>.<App1>/)
ok: [localhost] => (item=C:\<Maindir>\<App2>\Logs\platform.log.2020-09-18) => {
"msg": [
"src: C:\\<Maindir>\\<App2>\\Logs\\platform.log.2020-09-18",
"dest: /<LinuxMainDir>/<logs>/Applications/<serverName>.<App2>/"
]
}
skipping: [localhost] => (item=/<LinuxMainDir>/<logs>/Applications/<serverName>.<App2>/)
ok: [localhost] => (item=C:\<Maindir>\<App3>\Logs\platform.log.2020-09-18) => {
"msg": [
"src: C:\\<Maindir>\\<App3>\\Logs\\platform.log.2020-09-18",
"dest: /<LinuxMainDir>/<logs>/Applications/<serverName>.<App3>/"
]
}
skipping: [localhost] => (item=/<LinuxMainDir>/<logs>/Applications/<serverName>.<App3>/)
ok: [localhost] => (item=C:\<Maindir>\<App4>\Logs\platform.log.2020-09-18) => {
"msg": [
"src: C:\\<Maindir>\\<App4>\\Logs\\platform.log.2020-09-18",
"dest: /<LinuxMainDir>/<logs>/Applications/<serverName>.<App4>/"
]
}
skipping: [localhost] => (item=/<LinuxMainDir>/<logs>/Applications/<serverName>.<App4>/)
PLAY RECAP ********************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Another idea would be to use Python array slicing to separate odd from even elements and Ansible zip filter.
This would be the playbook:
- hosts: all
gather_facts: no
tasks:
- debug:
msg:
- "src: {{ item.0 }}"
- "dest: {{ item.1 }}"
loop: "{{ result.stdout_lines[::2] | zip(result.stdout_lines[1::2]) | list }}"
vars:
result:
stdout_lines:
- "C:\\<Maindir>\\<App1>\\Logs\\platform.log.2020-09-18"
- "/<LinuxMainDir>/<logs>/Applications/<serverName>.<App1>/"
- "C:\\<Maindir>\\<App2>\\Logs\\platform.log.2020-09-18"
- "/<LinuxMainDir>/<logs>/Applications/<serverName>.<App2>/"
- "C:\\<Maindir>\\<App3>\\Logs\\platform.log.2020-09-18"
- "/<LinuxMainDir>/<logs>/Applications/<serverName>.<App3>/"
- "C:\\<Maindir>\\<App4>\\Logs\\platform.log.2020-09-18"
- "/<LinuxMainDir>/<logs>/Applications/<serverName>.<App4>/"
This yields the recap:
PLAY [all] ********************************************************************************************************
TASK [debug] ******************************************************************************************************
ok: [localhost] => (item=['C:\\<Maindir>\\<App1>\\Logs\\platform.log.2020-09-18', '/<LinuxMainDir>/<logs>/Applications/<serverName>.<App1>/']) => {
"msg": [
"src: C:\\<Maindir>\\<App1>\\Logs\\platform.log.2020-09-18",
"dest: /<LinuxMainDir>/<logs>/Applications/<serverName>.<App1>/"
]
}
ok: [localhost] => (item=['C:\\<Maindir>\\<App2>\\Logs\\platform.log.2020-09-18', '/<LinuxMainDir>/<logs>/Applications/<serverName>.<App2>/']) => {
"msg": [
"src: C:\\<Maindir>\\<App2>\\Logs\\platform.log.2020-09-18",
"dest: /<LinuxMainDir>/<logs>/Applications/<serverName>.<App2>/"
]
}
ok: [localhost] => (item=['C:\\<Maindir>\\<App3>\\Logs\\platform.log.2020-09-18', '/<LinuxMainDir>/<logs>/Applications/<serverName>.<App3>/']) => {
"msg": [
"src: C:\\<Maindir>\\<App3>\\Logs\\platform.log.2020-09-18",
"dest: /<LinuxMainDir>/<logs>/Applications/<serverName>.<App3>/"
]
}
ok: [localhost] => (item=['C:\\<Maindir>\\<App4>\\Logs\\platform.log.2020-09-18', '/<LinuxMainDir>/<logs>/Applications/<serverName>.<App4>/']) => {
"msg": [
"src: C:\\<Maindir>\\<App4>\\Logs\\platform.log.2020-09-18",
"dest: /<LinuxMainDir>/<logs>/Applications/<serverName>.<App4>/"
]
}
PLAY RECAP ********************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
When I would have a slight preference for the second approach, I could understand it could be less readable for people not used to Python's array slicing.

Split debug variable output into two separate variables in ansible

I am using below code snippet where image details would be printed:
- set_fact:
image_name: "{{ load.results|map(attribute='stdout_lines')|list }}"
- debug:
var: image_name
Output:
TASK [set_fact] ***************************************************************************************************************************************************************************
ok: [xx.xx.xx.xx]
TASK [debug] ******************************************************************************************************************************************************************************
ok: [xx.xx.xx.xx] => {
"image_name": [
[
"Loaded image(s): localhost/cim:v1.5"
],
[
"Loaded image(s): localhost/cim:v1.8"
]
]
}
Is there a way I can store the image name and tag in two separate variables under set_fact itself or in any other form so I can reuse those 2 variables for the next task?
You can use a regex_findall filter in order to achieve this.
The regex used here is (\S*):(\S+). if needed, more explanation on it can be found here
Given the playbook:
- hosts: all
gather_facts: no
vars:
load:
results:
- stdout_lines:
- "Loaded image(s): localhost/cim:v1.5"
- stdout_lines:
- "Loaded image(s): localhost/cim:v1.8"
tasks:
- set_fact:
images: "{{ images | default([]) + item | regex_findall('(\\S*):(\\S+)') }}"
loop: "{{ load.results | map(attribute='stdout_lines') | flatten }}"
- debug:
msg: "This image repository is `{{ item.0 }}` and its tag is `{{ item.1 }}`"
loop: "{{ images }}"
This yields the recap:
PLAY [all] *********************************************************************************************************
TASK [set_fact] ****************************************************************************************************
ok: [localhost] => (item=Loaded image(s): localhost/cim:v1.5)
ok: [localhost] => (item=Loaded image(s): localhost/cim:v1.8)
TASK [debug] *******************************************************************************************************
ok: [localhost] => (item=['localhost/cim', 'v1.5']) => {
"msg": "This image repository is `localhost/cim` and its tag is `v1.5`"
}
ok: [localhost] => (item=['localhost/cim', 'v1.8']) => {
"msg": "This image repository is `localhost/cim` and its tag is `v1.8`"
}
PLAY RECAP *********************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Ansible: move on to the next task if the task is completed on one host

In ansible what i require is to check for a file is available on two hosts. But if the file is available on even one host i need to cancel the task on other host and move onto the next task. The reason why i require this is because a the next task can only be done if that particular file is available and that file can be randomly written to any of the hosts.
The following play does exactly what you want:
---
- hosts:
- server1
- server2
gather_facts: False
vars:
file_name: 'foo.bar'
tasks:
- name: wait for file
wait_for:
path: '{{ file_name }}'
state: present
timeout: 30
ignore_errors: True
- name: stat
stat:
path: '{{ file_name }}'
register: result
- name: next
debug:
msg: "File {{ file_name }} available on {{ ansible_host }}"
when: result.stat.isreg is defined and result.stat.isreg
The output is:
PLAY [server1,server2] *********************************************************
TASK [wait for file] ***********************************************************
ok: [server1]
fatal: [server2]: FAILED! => {"changed": false, "elapsed": 3, "msg": "Timeout when waiting for file foo.bar"}
...ignoring
TASK [stat] ********************************************************************
ok: [server1]
ok: [server2]
TASK [next] ********************************************************************
skipping: [server2]
ok: [server1] => {
"msg": "File foo.bar available on server1"
}
PLAY RECAP *********************************************************************
server1 : ok=3 changed=0 unreachable=0 failed=0
server2 : ok=0 changed=0 unreachable=0 failed=0
You can use the stat module to check the status like below and for also you can add the serial:1 below hosts: in your playbook
stat:
path: /path/to/something
register: p
debug:
msg: "Path exists and is a directory"
when: p.stat.isdir is defined and p.stat.isdir
https://docs.ansible.com/ansible/latest/modules/stat_module.html for more details

Resources