Equivalent of rm -Rf deleting subfolders

Hi All,

I’m proudly getting used to my first Ansible playbooks, but I still have an issue with deletion of a folder content together with all subfolders.

I need the equivalent of

rm -Rf /home/guest/*
rm -Rf /home/guest/.*

and my playbook deletes files (including hidden ones) but not subfolders

---
- name: Empty a dir
  hosts: test
  tasks:
    - name: Find content of /home/guest/
      ansible.builtin.find:
        paths: /home/guest/
        hidden: yes
        recurse: yes
      register: contenu_rep
    - name: Delete found content
      ansible.builtin.file:
        path: "{{ item.path }}"
        state: absent
      loop: "{{ contenu_rep.files }}"

I’ve read about deleting the folder itself and then re-create it, but I must keep my folder (creation times, ownership, permissions, etc).

What am I missing to delete the whole content of /home/guest/ , including subfolders, but keep /home/guest itself?

Shouldn’t it work with the recurse ?

Thank you!

PS: I’d like to avoid just alling a rm script, both for my own learning of Ansible, and to benefit from error handling at Ansible level.

1 Like

Hi @cndpansible, after poking around with this a little, think I was able to make it behave the way you’re asking for by adding file_type: any to the ansible.builtin.find block as per the module documentation.

During my initial testing, and confirmed by my reading of that page, ansible.builtin.find defaults to finding files. That, combined with recurse: true was finding files at all levels, but not the directories.

I think that explains why it was behaving that way.

Hope that helps.

1 Like

Doing it the ansible way will work, but if your folder contains many items then it will get slow very quickly. In practice for me this only worked OK up to a few hundred files.
For anything more I used ansible.builtin.command and rm -rf

1 Like

Hi All,
Thanks a lot @Lyle_McKarns and @dnmvisser

Indeed, Ansible does not delete directories, but it empties them fine.
This is perfectly OK to me, I will work to understand the solutions but might stick to the original syntax.

Thanks as well for the warning about timing. I tested and indeed, emptying ~/.cache (even if not that big) takes a long time :smile:

Since an approach like

- name: Delete all content (including directory)
  ansible.builtin.file:
    path: /home/guest/
	state: absent

is not feasible, you could take advantage of Developing modules and Create a custom module. Find a prototype here:

Custom Module library/rm_r.py

#!/usr/bin/python

from __future__ import (absolute_import, division, print_function)
__metaclass__ = type

from ansible.module_utils.basic import AnsibleModule

import os, shutil

def run_module():
    module_args = dict(
        path=dict(type='str', required=True)
    )

    result = dict(
        changed=False,
        found_path=''
    )

    module = AnsibleModule(
        argument_spec=module_args,
        supports_check_mode=True
    )

    if module.check_mode:
        module.exit_json(**result)

    folder = module.params['path']

    # Main logic

    for f in os.listdir(folder):
        full_path = os.path.join(folder, f)
        if os.path.isfile(full_path) or os.path.islink(full_path):
            os.unlink(full_path)
        elif os.path.isdir(full_path):
            shutil.rmtree(full_path)
        result['found_path'] = result['found_path'] + full_path + '\n'
        result['changed'] = True

    module.exit_json(**result)

def main():
    run_module()

if __name__ == '__main__':
    main()

Playbook rm_r.yml

---
- hosts: localhost
  become: false
  gather_facts: false

  tasks:

  - name: Delete directory content
    rm_r:
      path: "/home/guest/foo/"
    register: result

  - debug:
      msg: "{{ result }}"

For a simple test

tree foo/
foo/
├── bar
├── one.tst
└── two.tst

it will result into an output of

TASK [debug] ************************
ok: [localhost] =>
  msg:
    changed: true
    failed: false
    found_path: |-
      /home/guest/foo/bar
      /home/guest/foo/one.tst
      /home/guest/foo/two.tst
	  
Delete directory content ----- 0.38s
debug ------------------------ 0.03s

deleted files and folder, and leave an empty directory

tree foo/
foo/

0 directories, 0 files

Just add error handling, enhance the behavior and perform further tests as necessary.

1 Like

The in my other answer given approach with a Custom Module will have significant performance adavantages

and it is the recommended Ansible way for such Use Cases.

1 Like

I did a little more digging, and if what you’re really after is “delete everything from this directory” without taking a huge performance hit like like @dnmvisser mentioned, you can simply add file_type: any and remove recurse (and keep hidden: true). This will return all files, and all top level directories.

And ansible.builtin.file will delete a directory and its content with state: absent

1 Like

Great, I understand, covering all “file types” will include directories, and removing them at top level (no recursion needed) won’t impact performance, correct?

Thanks a lot!!!