143

How can I copy more than a single file into remote nodes by Ansible in a task?

I've tried to duplicate the copy module line in my task to define files but it only copies the first file.

Paul Hodges
  • 13,382
  • 1
  • 17
  • 36
Mark K.
  • 1,969
  • 2
  • 16
  • 15

15 Answers15

169

You can use the with_fileglob loop for this:

- copy:
    src: "{{ item }}"
    dest: /etc/fooapp/
    owner: root
    mode: 600
  with_fileglob:
    - "/playbooks/files/fooapp/*"
Kevin C
  • 4,851
  • 8
  • 30
  • 64
Arbab Nazar
  • 22,378
  • 10
  • 76
  • 82
  • 5
    This approach could help me if I've had all of my files in the same root for copying into remote machine, what about having some files in various directories. For example, I wanna copy 3 files from 3 different directory – Mark K. Apr 19 '16 at 07:37
  • Hey, I'm trying to move all the files from my `/roles/db/files` but I can't get it working with this method. I've tried `with_fileglob: - /roles/db/file/*` but it won't fine the path – Batman Dec 18 '17 at 20:12
  • 1
    The big disadvantage with this method is that it flattens the directory structure. – Quintin Par Sep 10 '18 at 02:32
  • **Note** that `loop` is recommended over `with_*`, see [Ansible Playbooks - Loops](https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_loops.html) documentation. – Flow Mar 22 '23 at 15:53
162
- name: copy multiple items
    copy: 
      src: "{{ item.src }}" 
      dest: "{{ item.dest }}"
    loop:
      - src: containerizers
        dest: /etc/mesos/containerizers
      - src: another_file
        dest: /etc/somewhere
      - src: dynamic
        dest: "{{ var_path }}"
guenhter
  • 11,255
  • 3
  • 35
  • 66
  • Is it possible to have this solution with the `dest` set as a variable? `{ src: 'containerizers', dest: {{ containerizers }} }`. – Gesias Jan 19 '17 at 17:03
  • 2
    @Gesias, yes. Actually, both sides can be variables: `{ src: '{{ source.var }}', dest: '{{ dest.var }}' }` – ntwrkguru Apr 20 '17 at 01:36
48

Since Ansible 2.5 the with_* constructs are not recommended, and loop syntax should be used. A simple practical example:

- name: Copy CA files
  copy:
    src: '{{item}}'
    dest: '/etc/pki/ca-trust/source/anchors'
    owner: root
    group: root
    mode: 0644
  loop:
    - symantec-private.crt
    - verisignclass3g2.crt

Please note that in many cases a specific list of files would be preferred over globbing (using wildcards). If specific files are not known, with_fileglob would still be suggested over a loop with lookup.

Richlv
  • 3,954
  • 1
  • 17
  • 21
  • 1
    On the [page you linked](https://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html#migrating-from-with-x-to-loop) it explicitly says that `with_*` is **not** deprecated: “We have not deprecated the use of `with_` - that syntax will still be valid for the foreseeable future.” (As of 11/2021) They only *recommend* it. – jotaen Nov 11 '21 at 15:55
  • 2
    Thank you for spotting that - the notice was not there before, it was added at some point in late 2019. Will amend the answer accordingly. – Richlv Nov 11 '21 at 19:28
  • The page linked in this answer gives `with_fileglob` as an example of a `with_*` construct that would be preferred over a `loop`, near the bottom of the page. – Arthur Hebert-Ryan Mar 05 '23 at 07:57
  • Thanks, slightly amended the answer to include globbing. – Richlv Mar 06 '23 at 11:18
18

You can use with_together for this purpose:

- name: Copy multiple files to multiple directories
  copy: src={{ item.0 }} dest={{ item.1 }}
  with_together:
    - [ 'file1', 'file2', 'file3' ]
    - [ '/dir1/', '/dir2/', '/dir3/' ]
Mircea Husz
  • 197
  • 2
14

If you need more than one location, you need more than one task. One copy task can copy only from one location (including multiple files) to another one on the node.

- copy: src=/file1 dest=/destination/file1
- copy: src=/file2 dest=/destination/file2

# copy each file over that matches the given pattern
- copy: src={{ item }} dest=/destination/
  with_fileglob:
    - /files/*
flxPeters
  • 1,476
  • 12
  • 21
  • - name: copy file1 copy: src=/file1 dest=/destination/file1 - name: copy file2 copy: src=/file2 dest=/destination/file2 – Mark K. Apr 21 '16 at 12:30
  • Depends. Simpler and likely cleaner, but can be done using more complex data structures, such as a list of anonymous dictionaries with source and target data, looped with_items. It's the same in any language - you have to make a judgment call. There are cases where a delegation function is more efficient and maintainable than a long series of copy/pasted if statements. I'm that freak that would rather maintain the concise bit of well structured code than a long and tedious list of nearly-identical directives, but I don't assume everyone agrees. Do what's maintainable for you. – Paul Hodges Jul 25 '18 at 13:51
10

You can use a find, and then copy those files.

---
- hosts: lnx
  tasks:
    - find: 
        paths: /appl/scripts/inq
        recurse: true
        patterns: "inq.Linux*"
      register: file_to_copy

     - copy: 
         src: "{{ item.path }}" 
         dest: /usr/local/sbin/
         owner: root
         mode: 0775
       loop: "{{ files_to_copy.files }}"
Kevin C
  • 4,851
  • 8
  • 30
  • 64
  • just a side note that `find` module only work for ansible 2.x but not for ansible 1.x – Arbab Nazar Aug 09 '16 at 07:02
  • I have fixed your answer becuase you have mentioned the `stdout_lines` in the return value but is not applicable for `find` module. It only have `files`, `examined` and `matched` as return values. Hope that help others – Arbab Nazar Aug 09 '16 at 07:09
  • 2
    Has anybody been able to get this working to copy files to remote nodes? `find` only seems to look at the remote system, not allowing to grab anything from the managing node. These answers, using `with_fileglob`, seems to be more fitting: https://stackoverflow.com/a/42290160/272387 , https://stackoverflow.com/a/36720342/272387 . – Richlv Oct 30 '17 at 15:16
6
- name: find inq.Linux*
  find:  paths="/appl/scripts/inq" recurse=yes patterns="inq.Linux*"
  register: find_files


- name: set fact
  set_fact:
    all_files:
      - "{{ find_files.files | map(attribute='path') | list }}"
  when: find_files > 0


- name: copy files
  copy:
    src: "{{ item }}"
    dest: /destination/
  with_items: "{{ all_files }}"
  when: find_files > 0
  • 8
    A good answer contains not only code, but also some explanations or documentation references. – Laurenz Albe Aug 10 '18 at 07:48
  • The find, set_fact and copy modules are all explained in the ansible documentation. Read about filters, also explained in the ansible documentation if you whant to know more about this section (- "{{ find_files.files | map(attribute'path') | list }}") – Fredric Andersson Aug 10 '18 at 08:54
  • Copy assumes a local source, so to make this example self-consistent, either the find option needs to be run once on localhost, or else the final copy operation needs to specify a remote src. The nice thing about this approach is that it makes the file list available for other purposes (e.g. deleting later if the copy is only temporary). – ncoghlan Apr 19 '21 at 07:47
5

Or you can use with_items:

- copy:
    src: "{{ item }}"
    dest: /etc/fooapp/
    owner: root
    mode: 600
  with_items:
    - dest_dir
MxWild
  • 3,080
  • 2
  • 14
  • 15
3

copy module is a wrong tool for copying many files and/or directory structure, use synchronize module instead which uses rsync as backend. Mind you, it requires rsync installed on both controller and target host. It's really powerful, check ansible documentation.

Example - copy files from build directory (with subdirectories) of controller to /var/www/html directory on target host:

synchronize:
  src: ./my-static-web-page/build/
  dest: /var/www/html
  rsync_opts:
    - "--chmod=D2755,F644" # copy from windows - force permissions
mrówa
  • 5,671
  • 3
  • 27
  • 39
2

You can loop through variable with list of directories:

- name: Copy files from several directories
  copy:
    src: "{{ item }}"
    dest: "/etc/fooapp/"
    owner: root
    mode: "0600"
  loop: "{{ files }}"
  vars:
    files:
      - "dir1/"
      - "dir2/"
Stan
  • 21
  • 1
2

Use the following source code for copy multiple files on your client machine.


 - name: Copy data to the client machine
   hosts: hostname
   become_method: sudo
   become_user: root
   become: true
   tasks: 
     # Copy twice as sometimes files get skipped (mostly only one file skipped from a folder if the folder does not exist)
     - name: Copy UFO-Server 
       copy:
         src: "source files path"
         dest: "destination file path"
         owner: root
         group: root
         mode: 0644
         backup: yes
       ignore_errors: true

Note:

If you are passing multiple paths by using variable then

src: "/root/{{ item }}"

If you are passing path by using a variable for different items then

src: "/root/{{ item.source_path }}"

2

Copy files from multiple directories to multiple directories with Ansible

I found the guenhter answer helpful but needed to change also the remote files' mode. I don't have enough reputation to put this as a comment, which would be a more appropriate place for this. In the example, I copy two files from two directories into /tmp and /tmp/bin, which I create first and modify remote files mode.

- name: cpldupd
  hosts: test
  remote_user: root
  become: true
  vars:
    - rpth: /tmp
  tasks:
    - name: Create '{{rpth}}/bin'
      file:
        path: '{{rpth}}/bin'
        state: directory

    - name: Transfer
      copy: src={{ item.src }} dest={{ item.dest }} mode=0775
      with_items:
      - { src: '../utils/cpldupd', dest: '{{rpth}}/cpldupd' }
      - { src: '../utils/bin/cpldupd', dest: '{{rpth}}/bin/cpldupd' }
  • If you have a new question, please ask it by clicking the [Ask Question](https://stackoverflow.com/questions/ask) button. Include a link to this question if it helps provide context. - [From Review](/review/late-answers/30961802) – Andrew Richards Feb 08 '22 at 13:32
  • It's not a question but another more complete example of copying files from multiple directories to multiple directories with Ansible. It adds remote file mode. It could be more appropriate to add it as a comment but I do not have enough reputation yet. I see that you have also used this technique in the past https://stackoverflow.com/questions/28347717/how-to-create-an-empty-file-with-ansible/50005056#50005056 – Michael Zaidman Feb 10 '22 at 18:42
  • I was a bit harsh there, sorry: When I was reviewing the question it felt like you were wanting to highlight how to deal with file permissions but that wasn't in the original question (thus a new question could be a good option to highlight this); you're right that a comment feels more appropriate to put against the answer you've mentioned; also I saw that using `mode` was already in several of the other answers so no particular need to say it again. – Andrew Richards Feb 11 '22 at 15:27
1

Here is a generic solution for copying files:

   ...
    - name: Find files you want to move
      ansible.builtin.find:
        paths: /path/to/files/
        file_type: file
        excludes: "*.txt" # Whatever pattern you want to exclude
      register: files_output

    - name: Copy the files
      ansible.builtin.copy:
        src: "{{ item.path }}"
        dest: /destination/directory/
      loop: "{{ files_output.files }}"
   ...

This is more powerful than using with_fileglob as you can match using regexes. Here is this play in action:

$ ls /path/to/files
demo.yaml  test.sh  ignore.txt

$ ls /destination/directory
file.h

$ ansible-playbook playbook.yaml
...[some output]...

$ ls /destination/directory
file.h demo.yaml test.sh

As you can see from the above example, ignore.txt was not copied over to the destination directory because of the excludes regex in the playbook. Ignoring files like this is not possible as simply using with_fileglob.

Additionally, you can move files from multiple directories with relative ease:

   ...
    - name: Find files you want to move
      ansible.builtin.find:
        paths: /path/to/files/
        # ... the rest of the task
      register: list1

    - name: Find more files you want to move
      ansible.builtin.find:
        paths: /different/path/
        # ... the rest of the task
      register: list2

    - name: Copy the files
      ansible.builtin.copy:
        src: "{{ item.path }}"
        dest: /destination/directory/
      loop: "{{ list1.files + list2.files }}"
   ...
1

Here is a sample Ansible Script to copy multiple Files on remote Hosts

- name: Copy Multiple Files on remote Hosts
  ansible.windows.win_copy:
    src: "{{ srcPath }}/{{ item }}" # Remeber to us {{item}}
                                    # as a postfix to source path

    dest: "{{ destPath }}"
    remote_src: yes # if source path is available on remote Host
  with_items:
    - abc.txt
    - abc.properties
Oliver
  • 3,815
  • 8
  • 35
  • 63
hussains8
  • 634
  • 6
  • 12
0

  • hosts: test gather_facts: false become: true vars: path: '/home/ansibm/playbooks' remote_path: '/home/{{ansible_ssh_user}}' dir: 'yml_files' tasks:
    • name: "creating directory for backup file" file: path: '{{ remote_path }}/{{ dir }}' state: directory owner: '{{ansible_ssh_user}}' group: '{{ansible_ssh_user}}' mode: 0700
    • name: "copying yml files" copy: src: '{{item}}' dest: '{{ remote_path }}/{{ dir }}' owner: '{{ansible_ssh_user}}' group: '{{ansible_ssh_user}}' mode: 0644 loop: - '{{ path }}/ab.html' - '{{ path }}/cp.yml'
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Feb 02 '22 at 08:27