Ansible delete Files with wildcard/regex/glob with exception - ansible

I want to delete files based on a wildcard but also add exceptions to the rule.
- hosts: all
tasks:
- name: Ansible delete file wildcard
find:
paths: /etc/wild_card/example
patterns: "*.txt"
use_regex: true
register: wildcard_files_to_delete
- name: Ansible remove file wildcard
file:
path: "{{ item.path }}"
state: absent
with_items: "{{ wildcard_files_to_delete.files }}"
For example I want to except a file named "important.txt". How can I do that?

Just add a when condition to the task that deletes files. E.g., something like:
- name: Ansible remove file wildcard
file:
path: "{{ item.path }}"
state: absent
when: item.path != '/etc/wild_card/example/important.txt'
with_items: "{{ wildcard_files_to_delete.files }}"
This will skip a specific file. If you have a list of files to skip you could do this instead:
- name: Ansible remove file wildcard
file:
path: "{{ item.path }}"
state: absent
when: item.path not in files_to_skip
with_items: "{{ wildcard_files_to_delete.files }}"
vars:
files_to_skip:
- /etc/wild_card/example/important.txt
- /etc/wild_card/example/saveme.txt
And if you want to preserve files based on some sort of pattern, you could make use of ansible's match or search tests:
- name: Ansible remove file wildcard
file:
path: "{{ item.path }}"
state: absent
when: item.path is not search('important.txt')
with_items: "{{ wildcard_files_to_delete.files }}"

Related

Ansible - loop over multiple items in stdout_lines

I am performing a grep with multiple items.
---
- hosts: my_host
gather_facts: false
vars:
my_list:
- whatever
- something
tasks:
- name: grep for item in search path
shell: "grep -rIL {{ item }} /tmp"
register: the_grep
loop: "{{ my_list }}"
- debug:
msg: "{{ item.stdout_lines }}"
loop: "{{ the_grep.results }}"
Depending on the result, multiple files could match.
msg:
- /tmp/something.conf
- /tmp/folder/file.txt
Q: How would I configure Ansible to loop over the items in stdout_lines?
The use case I'm solving is to delete .ini sections based on the item, but in this case, Ansible doesn't loop over the stdout_lines.
- name: remove stanza from ini file
ini_file:
path: "{{ item.stdout_lines }}"
section: "{{ item.item }}"
mode: '0600'
state: absent
loop: "{{ the_grep.results }}"
when: item.stdout_lines | length > 0
It seems that this doesn't work, but configuring item.stdout_lines[0] gives the partially expected result, since Ansible will use only the first item in that list. But ofc, not the 2nd and so on.
Perhaps there's a prettier answer, but solved it by using with_nested and creating a json_query:
- name: remove stanza from ini file
ini_file:
path: "{{ item.0 }}"
section: "{{ item.1.item }}"
mode: '0600'
state: absent
with_nested:
- "{{ the_grep | json_query('results[].stdout_lines[]') }}"
- "{{ the_grep.results }}"

how to pass many variables in ansible?

I faced problems when I don't know how to pass many variables in with_items:
I have these vars:
- vars:
paths:
- /tmp/tecom/python3/
- /tmp/tecom/pip/
- /tmp/tecom/psycopg2/
- /tmp/tecom/docker/
- /tmp/tecom/postgresql/
files:
- python3.tar
- pip.tar
- psycopg2.tar
- docker.tar
- postgresql.tar
And I have the task that should extract the archives:
- name: unarchive "{{ item }}"
unarchive:
src: "{{ item }}"
dest: # should take the paths above. Every path should match it's own `.tar` file.
with_items: "{{ files }}"
Every path should match its own .tar file.
For example,
- debug:
msg: "unarchive {{ item }} to {{ _path }}"
loop: "{{ files }}"
vars:
_path: "{{ path }}/{{ item|splitext|first }}/"
path: /tmp/tecom
files:
- python3.tar
- pip.tar
- psycopg2.tar
- docker.tar
- postgresql.tar
gives (abridged)
msg: unarchive python3.tar to /tmp/tecom/python3/
msg: unarchive pip.tar to /tmp/tecom/pip/
msg: unarchive psycopg2.tar to /tmp/tecom/psycopg2/
msg: unarchive docker.tar to /tmp/tecom/docker/
msg: unarchive postgresql.tar to /tmp/tecom/postgresql/
Q: "How do I unarchive those files to the folders?"
A: Use the expressions as appropriate, e.g.
- name: "unarchive {{ item }}"
unarchive:
src: "{{ item }}"
dest: "{{ path }}/{{ item|splitext|first }}/"
loop: "{{ files }}"
(not tested)
It's up to you where you put the variables files and path. See Variable precedence: Where should I put a variable?

In Ansible loop, test existence of files from registered results

I have several files that I need to backup in different directories. I have tried the code below and not working for me.
vars:
file_vars:
- {name: /file1}
- {name: /etc/file2}
- {name: /etc/file/file3}
tasks:
- name: "Checking if config files exists"
stat:
path: "{{ item.name }}"
with_items: "{{ file_vars }}"
register: stat_result
- name: Backup Files
copy: src={{ item.name }} dest={{ item.name }}{{ ansible_date_time.date }}.bak
with_items: "{{ file_vars }}"
remote_src: yes
when: stat_result.stat.exists == True
The problem is the condition
when: stat_result.stat.exists == True
There is no attribute stat_result.stat. Instead, the attribute stat_result.results is a list of the results from the loop. It's possible to create a dictionary of files and their statuses. For example
- set_fact:
files_stats: "{{ dict(my_files|zip(my_stats)) }}"
vars:
my_files: "{{ stat_result.results|json_query('[].item.name') }}"
my_stats: "{{ stat_result.results|json_query('[].stat.exists') }}"
Then simply use this dictionary in the condition
when: files_stats[item.name]
Below is a shorter version which creates the dictionary more efficiently
- set_fact:
files_stats: "{{ dict(stat_result.results|
json_query('[].[item.name, stat.exists]')) }}"
Please try using below worked for me:
---
- name: Copy files
hosts: localhost
become: yes
become_user: root
vars_files:
- files.yml
tasks:
- name: "Checking if config files exists"
stat:
path: "{{ item }}"
with_items: "{{ files }}"
register: stat_result
- name: Ansible
debug:
msg: "{{ stat_result }}"
- name: Backup Files
copy:
src: "{{ item }}"
dest: "{{ item.bak }}"
with_items: "{{ files }}"
when: stat_result == "True"
and files.yml will look like:
---
files:
- /tmp/file1
- /tmp/file2
you can check you playbook syntax using below command:
ansible-playbook copy.yml --syntax-check
Also you do dry run your playbook before actual execution.
ansible-playbook -i localhost copy.yml --check

win_find FAILED! => {"msg": "'dict object' has no attribute 'files'"}

I am trying to run win_find with variables for "paths" to find temp files (e.g. c:\users\public\appdata\local\temp) but when I pass it to debug I get the error message
"FAILED! => {"msg": "'dict object' has no attribute 'files'"}"
I have tried swapping the "register" and "with_items" lines but that seems to make no difference at all.
- hosts: windows-02
vars:
dir: \AppData\Local\Temp\
tasks:
- name: find user directories
win_find:
paths: c:\users
recurce: yes
file_type: directory
get_checksum: no
register: user_dir
- name: debug 1
debug:
msg: "{{ item.path + dir }}"
loop: "{{ user_dir.files }}"
- name: find temp files
win_find:
paths: "{{ item.path + dir }}"
recurce: yes
hidden: yes
get_checksum: no
register: files_to_delete
with_items: "{{ user_dir.files }}"
- name: debug
debug:
msg: "{{ item }}"
loop: "{{ files_to_delete.files }}"
- name: remove
win_file:
path: "{{ item.path }}"
state: absent
with_items: "{{ files_to_delete.files }}"
I expect to get a list of files to be deleted which will be passed to the "win_files" module. Instead I'm getting the error message
"FAILED! => {"msg": "'dict object' has no attribute 'files'"}"
There is a typo in win_find module, it is recurse: yes
Please find the below code whcih worked for me
---
- name: Find files
win_find:
paths: "{{paths}}"
age: "{{duration}}"
register: log_files
- name: Delete the files
win_file:
path: "{{item.path}}"
state: absent
with_items: "{{log_files.files}}"
Also files_to_delete will not have files_to_delete.files as it is in loop. It will have files_to_delete.results
So, it appears that win_find, at least in my case, does not return files when using "when_items". I got around this by creating a list of paths and passing it into the "paths" parameter of the module.
Here is my code:
- hosts: "{{ host }}"
vars:
dir: \AppData\Local\Temp\
temp_paths: []
line_break: \n
tasks:
- name: find user directories
win_find:
paths: c:\users
recurse: no
file_type: directory
get_checksum: no
register: user_dir
- name: debug 1
debug:
msg: "{{ item.path + dir }}"
loop: "{{ user_dir.files }}"
- name: set temp path
set_fact: temp_paths="{{temp_paths + [ item.path + dir ] }}"
when: item.path != 'C:\\users\\Public'
with_items: "{{ user_dir.files }}"
- name: find temp files
win_find:
paths: "{{ temp_paths }}"
recurse: True
patterns: '*'
hidden: False
get_checksum: False
register: files_to_delete
# ignore_errors: yes
- name: debug
debug:
msg: "{{ item.path }}"
loop: "{{ files_to_delete.files }}"
- name: remove
win_file:
path: "{{ item.path }}"
state: absent
with_items: "{{ files_to_delete.files }}"

Ansible, iterate over multiple registers and make extended use of build in methods

To start off I have all my variables defined in YAML
app_dir: "/mnt/{{ item.name }}"
app_dir_ext: "/mnt/{{ item.0.name }}"
workstreams:
- name: tigers
service_workstream: tigers-svc
app_sub_dir:
- inbound
- inbound/incoming
- inbound/error
- inbound/error/processed
- name: lions
service_workstream: lions-svc
app_sub_dir:
- inbound
- inbound/incoming
- inbound/error
- inbound/error/processed
You may note app_dir: "/mnt/{{ item.name }}" and app_dir_ext: "/mnt/{{ item.0.name }}" looking odd, so I originally had my variables set as below in YAML but decided to use the above mainly due to less lines in YAML when I have a large amount of workstreams.
workstreams:
- name: tigers
service_workstream: tigers-svc
app_dir: /mnt/tigers
...
I then have Ansible code to check if the directories exists, if not create them and apply permissions (!note, have taken this approach due to a ssh timeout on operation when using the file: module on a number of very big NFS mounted shares).
- name: Check workstreams app_dir
stat:
path: "{{ app_dir }}"
register: app_dir_status
with_items:
- "{{ workstreams }}"
- name: Check workstreams app_sub_dir
stat:
path: "{{ app_dir_ext }}/{{ item.1 }}/"
register: app_sub_dir_status
with_subelements:
- "{{ workstreams }}"
- app_sub_dir
- name: create workstreams app_dir
file:
path: "/mnt/{{ item.0.name }}"
state: directory
owner: "ftp"
group: "ftp"
mode: '0770'
recurse: yes
with_nested:
- '{{ workstreams }}'
- app_dir_status.results
when:
- '{{ item.1.stat.exists }} == false'
This is a little hacky but works, however I have a 2nd, 3rd, 4th path to check..etc
My question here is how to I update/refactor the above code to use <register_name>.stat.exists == false from both app_dir_status and app_sub_dir_status to control my task ?
You don't need to make nested loop! There's all required data inside app_sub_dir_status – just strip unnecessary items.
Here's simplified example:
---
- hosts: localhost
gather_facts: no
vars:
my_list:
- name: zzz1
sub:
- aaa
- ccc
- name: zzz2
sub:
- aaa
- bbb
tasks:
- stat:
path: /tmp/{{ item.0.name }}/{{ item.1 }}
register: stat_res
with_subelements:
- "{{ my_list }}"
- sub
- debug:
msg: "path to create '{{ item }}'"
with_items: "{{ stat_res.results | rejectattr('stat.exists') | map(attribute='invocation.module_args.path') | list }}"
You can iterate over stat_res.results | rejectattr('stat.exists') | list as well, but will have to construct path again as /tmp/{{ item.item.0.name }}/{{ item.item.1 }} – note double item, because first item is an element of stat_res.results which contain another item as element of your original loop for stat task.
P.S. Also I see no reason for your first task, as subdir task can detect all missing directories.

Resources