[ansible] Ansible: How to delete files and folders inside a directory?

The below code only deletes the first file it gets inside the web dir. I want to remove all the files and folders inside the web directory and retain the web directory. How can I do that?

  - name: remove web dir contents
     file: path='/home/mydata/web/{{ item }}' state=absent
     with_fileglob:
       - /home/mydata/web/*

Note: I've tried rm -rf using command and shell, but they don't work. Perhaps I am using them wrongly.

Any help in the right direction will be appreciated.

I am using ansible 2.1.0.0

This question is related to ansible delete-file delete-directory

The answer is


I want to make sure that the find command only deletes everything inside the directory and leave the directory intact because in my case the directory is a filesystem. The system will generate an error when trying to delete a filesystem but that is not a nice option. Iam using the shell option because that is the only working option I found so far for this question.

What I did:

Edit the hosts file to put in some variables:

[all:vars]
COGNOS_HOME=/tmp/cognos
find=/bin/find

And create a playbook:

- hosts: all
  tasks:
  - name: Ansible remove files
    shell: "{{ find }} {{ COGNOS_HOME }} -xdev -mindepth 1 -delete"

This will delete all files and directories in the COGNOS_HOME variable directory/filesystem. The "-mindepth 1" option makes sure that the current directory will not be touched.


Assuming you are always in Linux, try the find cmd.

- name: Clean everything inside {{ item }}
  shell: test -d {{ item }} && find {{ item }} -path '{{ item }}/*' -prune -exec rm -rf {} \;
  with_items: [/home/mydata/web]

This should wipe out files/folders/hidden under /home/mydata/web


That's what I come up with:

- name: Get directory listing
  find:
    path: "{{ directory }}" 
    file_type: any
    hidden: yes
  register: directory_content_result

- name: Remove directory content
  file:
    path: "{{ item.path }}" 
    state: absent
  with_items: "{{ directory_content_result.files }}" 
  loop_control:
    label: "{{ item.path }}" 

First, we're getting directory listing with find, setting

  • file_type to any, so we wouldn't miss nested directories and links
  • hidden to yes, so we don't skip hidden files
  • also, do not set recurse to yes, since it is not only unnecessary, but may increase execution time.

Then, we go through that list with file module. It's output is a bit verbose, so loop_control.label will help us with limiting output (found this advice here).


But I found previous solution to be somewhat slow, since it iterates through the content, so I went with:

- name: Get directory stats
  stat:
    path: "{{ directory }}"
  register: directory_stat

- name: Delete directory
  file:
    path: "{{ directory }}"
    state: absent

- name: Create directory
  file:
    path: "{{ directory }}"
    state: directory
    owner: "{{ directory_stat.stat.pw_name }}"
    group: "{{ directory_stat.stat.gr_name }}"
    mode: "{{ directory_stat.stat.mode }}"
  • get directory properties with the stat
  • delete directory
  • recreate directory with the same properties.

That was enough for me, but you can add attributes as well, if you want.


While Ansible is still debating to implement state = empty https://github.com/ansible/ansible-modules-core/issues/902

my_folder: "/home/mydata/web/"
empty_path: "/tmp/empty"


- name: "Create empty folder for wiping."
  file:
    path: "{{ empty_path }}" 
    state: directory

- name: "Wipe clean {{ my_folder }} with empty folder hack."
  synchronize:
    mode: push

    #note the backslash here
    src: "{{ empty_path }}/" 

    dest: "{{ nl_code_path }}"
    recursive: yes
    delete: yes
  delegate_to: "{{ inventory_hostname }}"

Note though, with synchronize you should be able to sync your files (with delete) properly anyway.


Using file glob also it will work. There is some syntax error in the code you posted. I have modified and tested this should work.

- name: remove web dir contents
  file:
    path: "{{ item }}"
    state: absent
  with_fileglob:
    - "/home/mydata/web/*"

There is an issue open with respect to this.

For now, the solution works for me: create a empty folder locally and synchronize it with the remote one.

Here is a sample playbook:

- name: "Empty directory"
  hosts: *
  tasks:
    - name: "Create an empty directory (locally)"
      local_action:
        module: file
        state: directory
        path: "/tmp/empty"

    - name: Empty remote directory
      synchronize:
        src: /tmp/empty/
        dest: /home/mydata/web/
        delete: yes
        recursive: yes

Isn't it that simple ... tested working ..

eg.

---
- hosts: localhost
  vars:
     cleandir: /var/lib/cloud/
  tasks:
   - shell: ls -a -I '.' -I '..' {{ cleandir }}
     register: ls2del
     ignore_errors: yes
   - name: Cleanup {{ cleandir }}
     file:
       path: "{{ cleandir }}{{ item }}"
       state: absent
     with_items: "{{ ls2del.stdout_lines }}"

Created an overall rehauled and fail-safe implementation from all comments and suggestions:

# collect stats about the dir
- name: check directory exists
  stat:
    path: '{{ directory_path }}'
  register: dir_to_delete

# delete directory if condition is true
- name: purge {{directory_path}}
  file:
    state: absent
    path: '{{ directory_path  }}'
  when: dir_to_delete.stat.exists and dir_to_delete.stat.isdir

# create directory if deleted (or if it didn't exist at all)
- name: create directory again
  file:
    state: directory
    path: '{{ directory_path }}'
  when: dir_to_delete is defined or dir_to_delete.stat.exist == False

I have written an custom ansible module to cleanup files based on multiple filters like age, timestamp, glob patterns, etc.

It is also compatible with ansible older versions. It can be found here.

Here is an example:

- cleanup_files:
  path_pattern: /tmp/*.log
  state: absent
  excludes:
    - foo*
    - bar*

Using shell module (idempotent too):

- shell: /bin/rm -rf /home/mydata/web/*

If there are dot/hidden files:

- shell: /bin/rm -rf /home/mydata/web/* /home/mydata/web/.*

Cleanest solution if you don't care about creation date and owner/permissions:

- file: path=/home/mydata/web state=absent
- file: path=/home/mydata/web state=directory

Remove the directory (basically a copy of https://stackoverflow.com/a/38201611/1695680), Ansible does this operation with rmtree under the hood.

- name: remove files and directories
  file:
    state: "{{ item }}"
    path: "/srv/deleteme/"
    owner: 1000  # set your owner, group, and mode accordingly
    group: 1000
    mode: '0777'
  with_items:
    - absent
    - directory

If you don't have the luxury of removing the whole directory and recreating it, you can scan it for files, (and directories), and delete them one by one. Which will take a while. You probably want to make sure you have [ssh_connection]\npipelining = True in your ansible.cfg on.

- block:
  - name: 'collect files'
    find:
      paths: "/srv/deleteme/"
      hidden: True
      recurse: True
      # file_type: any  # Added in ansible 2.3
    register: collected_files

  - name: 'collect directories'
    find:
      paths: "/srv/deleteme/"
      hidden: True
      recurse: True
      file_type: directory
    register: collected_directories

  - name: remove collected files and directories
    file:
      path: "{{ item.path }}"
      state: absent
    with_items: >
      {{
        collected_files.files
        + collected_directories.files
      }}

????????,????????,????????
  - name: delete old data and clean cache
    file:
      path: "{{ item[0] }}" 
      state: "{{ item[1] }}"
    with_nested:
      - [ "/data/server/{{ app_name }}/webapps/", "/data/server/{{ app_name }}/work/" ]
      - [ "absent", "directory" ]
    ignore_errors: yes

Below code worked for me :

- name: Get directory listing
  become: yes
  find:
    paths: /applications/cache
    patterns: '*'
    hidden: yes
  register: directory_content_result

- name: Remove directory content
  become: yes
  file:
    path: "{{ item.path }}"
    state: absent
  with_items: "{{ directory_content_result.files }}"

I really didn't like the rm solution, also ansible gives you warnings about using rm. So here is how to do it without the need of rm and without ansible warnings.

- hosts: all
  tasks:
  - name: Ansible delete file glob
    find:
      paths: /etc/Ansible
      patterns: "*.txt"
    register: files_to_delete

  - name: Ansible remove file glob
    file:
      path: "{{ item.path }}"
      state: absent
    with_items: "{{ files_to_delete.files }}"

source: http://www.mydailytutorials.com/ansible-delete-multiple-files-directories-ansible/


Just a small cleaner copy & paste template of ThorSummoners answer, if you are using Ansible >= 2.3 (distinction between files and dirs not necessary anymore.)

- name: Collect all fs items inside dir
  find:
    path: "{{ target_directory_path }}"
    hidden: true
    file_type: any
  changed_when: false
  register: collected_fsitems
- name: Remove all fs items inside dir
  file:
    path: "{{ item.path }}"
    state: absent
  with_items: "{{ collected_fsitems.files }}"
  when: collected_fsitems.matched|int != 0

This is my example.

  1. Install repo
  2. Install rsyslog package
  3. stop rsyslog
  4. delete all files inside /var/log/rsyslog/
  5. start rsyslog

    - hosts: all
      tasks:
        - name: Install rsyslog-v8 yum repo
          template:
            src: files/rsyslog.repo
            dest: /etc/yum.repos.d/rsyslog.repo
    
        - name: Install rsyslog-v8 package
          yum:
            name: rsyslog
            state: latest
    
        - name: Stop rsyslog
          systemd:
            name: rsyslog
            state: stopped
    
        - name: cleann up /var/spool/rsyslog
          shell: /bin/rm -rf /var/spool/rsyslog/*
    
        - name: Start rsyslog
          systemd:
            name: rsyslog
            state: started
    

Below code will delete the entire contents of artifact_path

- name: Clean artifact path
  file:
    state: absent
    path: "{{ artifact_path }}/"

Note: this will delete the directory too.


Below worked for me,

- name: Ansible delete html directory
  file:
   path: /var/www/html
   state: directory

try the below command, it should work

- shell: ls -1 /some/dir
  register: contents

- file: path=/some/dir/{{ item }} state=absent
  with_items: {{ contents.stdout_lines }}

Examples related to ansible

Specifying ssh key in ansible playbook file Ansible: how to get output to display How to do multiline shell script in Ansible Accessing inventory host variable in Ansible playbook Ansible: get current target host's IP address Ansible Ignore errors in tasks and fail at end of the playbook if any tasks had errors Ansible: How to delete files and folders inside a directory? How to pass a user / password in ansible command How to test that a registered variable is not empty? Copy multiple files with Ansible

Examples related to delete-file

Ansible: How to delete files and folders inside a directory? Can I delete data from the iOS DeviceSupport directory? Python3 project remove __pycache__ folders and .pyc files How do I delete files programmatically on Android? Delete files or folder recursively on Windows CMD How to delete a folder and all contents using a bat file in windows? How to delete a file or folder? How to remove a directory from git repository? Linux delete file with size 0 Java 'file.delete()' Is not Deleting Specified File

Examples related to delete-directory

Ansible: How to delete files and folders inside a directory? How to delete a folder with files using Java How to delete directory content in Java? How to delete a whole folder and content? How do I recursively delete a directory and its entire contents (files + sub dirs) in PHP? SVN undo delete before commit How to recursively delete an entire directory with PowerShell 2.0? How to fix "containing working copy admin area is missing" in SVN? In Unix, how do you remove everything in the current directory and below it? Delete directories recursively in Java