How to Exclude Specific Files When Copying Directories in Ansible


2 views

When working with Ansible's copy module, you might need to copy an entire directory while excluding specific files or subdirectories. The official documentation doesn't explicitly cover this scenario, which can be frustrating when dealing with directories containing hundreds of files.

with_fileglob Loop /h2>

One approach is to combine the copy module with a with_fileglob loop to selectively copy files:


- name: Copy all files except one
  copy:
    src: "{{ item }}"
    dest: /destination/path/
  with_fileglob:
    - "/source/path/*"
    - "!/source/path/excluded_file"

find Module /h2>

For more complex exclusion patterns, you can use the find module first to generate a file list:


- name: Find all files except the one to exclude
  find:
    paths: /source/path
    patterns: "*"
    excludes: "excluded_file"
    recurse: yes
  register: files_to_copy

- name: Copy the filtered files
  copy:
    src: "{{ item.path }}"
    dest: "/destination/path/{{ item.path | basename }}"
  loop: "{{ files_to_copy.files }}"

synchronize /h2>

For large directories, the synchronize module (which uses rsync) might be more efficient:


- name: Sync directory with exclusion
  synchronize:
    src: /source/path/
    dest: /destination/path/
    exclude: excluded_file

When you need to exclude multiple patterns, you can use lists:


- name: Exclude multiple files
  synchronize:
    src: /source/path/
    dest: /destination/path/
    exclude:
      - "*.tmp"
      - "*.log"
      - "secret_file"

For directories with thousands of files, the synchronize approach will generally be faster than iterating with copy. However, remember that synchronize requires rsync to be installed on both the control and target machines.


When working with Ansible's copy module, you might encounter scenarios where you need to recursively copy all contents of a directory except specific files or subdirectories. The official documentation doesn't explicitly cover this exclusion pattern, especially when dealing with large directory structures where manual enumeration isn't practical.

One effective approach combines Ansible's with_fileglob loop with conditional excludes:


- name: Copy all files except excluded patterns
  ansible.builtin.copy:
    src: "{{ item }}"
    dest: /target/path/
  with_fileglob:
    - /source/path/*
    - /source/path/.*
  when: "item not in ['/source/path/excluded_file', '/source/path/excluded_dir']"

For more complex exclusion patterns, use the regex_filter parameter with the find module:


- name: Find all files except those matching pattern
  ansible.builtin.find:
    paths: /source/path
    recurse: yes
    patterns: "*"
    excludes: "*.tmp,*.bak"
    use_regex: yes
  register: files_to_copy

- name: Copy filtered files
  ansible.builtin.copy:
    src: "{{ item.path }}"
    dest: "/target/path/{{ item.path | regex_replace('^/source/path/', '') }}"
  loop: "{{ files_to_copy.files }}"

For large directory operations, consider synchronize module with rsync patterns:


- name: Sync directory with exclusions
  ansible.builtin.synchronize:
    src: /source/path/
    dest: /target/path/
    rsync_opts:
      - "--exclude=temp/"
      - "--exclude=*.log"

When dealing with hundreds of files:

  • The find + copy approach has better performance than multiple individual copy tasks
  • For cross-platform compatibility, test exclusion patterns on different OS environments
  • Consider pre-processing file lists in variables for complex exclusion logic