72

I am trying to copy the content of dist directory to nginx directory.

- name: copy html file
  copy: src=/home/vagrant/dist/ dest=/usr/share/nginx/html/

But when I execute the playbook it throws an error:

TASK [NGINX : copy html file] **************************************************
fatal: [172.16.8.200]: FAILED! => {"changed": false, "failed": true, "msg": "attempted to take checksum of directory:/home/vagrant/dist/"}

How can I copy a directory that has another directory and a file inside?

tambre
  • 4,625
  • 4
  • 42
  • 55
Asier Gomez
  • 6,034
  • 18
  • 52
  • 105
  • 1
    From the Ansible docs for the [copy module](http://docs.ansible.com/ansible/copy_module.html): "The copy module copies a file on the local box to remote locations". You say that there are two files in /home/vagrant/dist. Are those located on the host you are running Ansible from, or on the host with the IP 172.16.8.200? – Bruce P Feb 18 '16 at 18:03
  • Hello @BruceP yes the directory /home/vagrant/dist is in the remote machine, as #helloV says I have put remote_src=yes but the problem is that "copy" doesn't work with directorys, and inside the directory "dist" there is a file and a directory. How can I copy the file and the directory? Thanks – Asier Gomez Feb 19 '16 at 14:18
  • @Asier it does - directory_mode=yes – holms Feb 07 '18 at 22:04
  • @holms Yes I try my code but it doesn't copy if there are directories with files inside another directory. – Asier Gomez Feb 08 '18 at 08:27

12 Answers12

59

To copy a directory's content to another directory you CAN use ansibles copy module:

- name: Copy content of directory 'files'
  copy:
    src: files/    # note the '/' <-- !!!
    dest: /tmp/files/

From the docs about the src parameter:

If (src!) path is a directory, it is copied recursively...
... if path ends with "/", only inside contents of that directory are copied to destination.
... if it does not end with "/", the directory itself with all contents is copied.

andymel
  • 4,538
  • 2
  • 23
  • 35
  • 7
    +1 This should be the correct answer! No `command`, no creating a register of files to copy... Just using the `copy` module how it is intended. – cjnash Feb 11 '21 at 22:11
  • 2
    Just in case in the future it makes any difference, I used `ansible.builtin.copy:` instead of `copy:` because in Ansible's official documentation "we recommend you use the FQCN" is mentioned. – Krackout Dec 23 '22 at 10:25
56

You could use the synchronize module. The example from the documentation:

# Synchronize two directories on one remote host.
- synchronize:
    src: /first/absolute/path
    dest: /second/absolute/path
  delegate_to: "{{ inventory_hostname }}"

This has the added benefit that it will be more efficient for large/many files.

Wolkenarchitekt
  • 20,170
  • 29
  • 111
  • 174
Aidan Feldman
  • 5,205
  • 36
  • 46
  • I'd say you only need to use synchronize when you want to move huge amount of files from/to remote location, because it's using rsync, what's the point to use rsync locally? maybe there's, I don't know :) – holms Feb 12 '18 at 20:27
  • 3
    Is `inventory_hostname` a built in ansible variable or we set it somehow? – pkaramol Jun 06 '18 at 08:44
  • 1
    @pkaramol [Built in.](http://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#magic-variables-and-how-to-access-information-about-other-hosts) – Aidan Feldman Jun 06 '18 at 13:50
  • 4
    It is planed that `copy` module will support recursive copy with `remote_src: yes` in Ansible 2.8 (see [Allow copy module to work with recursive and remote_src](https://github.com/ansible/ansible/issues/14131)). – Younes Mar 05 '19 at 15:33
  • 1
    It is possible with ansibles copy module, have a look at my answer – andymel Feb 12 '21 at 10:37
45

EDIT: This solution worked when the question was posted. Later Ansible deprecated recursive copying with remote_src

Ansible Copy module by default copies files/dirs from control machine to remote machine. If you want to copy files/dirs in remote machine and if you have Ansible 2.0, set remote_src to yes

- name: copy html file
  copy: src=/home/vagrant/dist/ dest=/usr/share/nginx/html/ remote_src=yes directory_mode=yes
helloV
  • 50,176
  • 7
  • 137
  • 145
  • Inside "/home/vagrant/dist/" directory there is a file and another directory, I can copy the file, but if I copy bouth, it throws the next error: 'attempted to take checksum of directory:' what could it be? I think the copy only works with files and not with directorys. @helloV – Asier Gomez Feb 19 '16 at 13:49
  • 31
    "Currently remote_src does not support recursive copying" - See: http://docs.ansible.com/ansible/copy_module.html – Mircea Vutcovici Apr 13 '17 at 18:31
  • 2
    It is planed that `copy` module will support recursive copy with `remote_src: yes` in Ansible 2.8 (see [Allow copy module to work with recursive and remote_src](https://github.com/ansible/ansible/issues/14131)). – Younes Mar 05 '19 at 15:34
  • At least now it is possible with ansibles copy module, have a look at my answer – andymel Feb 12 '21 at 10:38
  • I am using Ansible 2.4.4, the "remote_src" attribute doesn't support recursive copying. The "directory_mode" attribute is probably for copying from LOCAL to REMOTE recursively. – GreenLake4964 Feb 10 '22 at 09:40
30

Resolved answer: To copy a directory's content to another directory I use the next:

- name: copy consul_ui files
  command: cp -r /home/{{ user }}/dist/{{ item }} /usr/share/nginx/html
  with_items:
   - "index.html"
   - "static/"

It copies both items to the other directory. In the example, one of the items is a directory and the other is not. It works perfectly.

Jay
  • 27
  • 7
Asier Gomez
  • 6,034
  • 18
  • 52
  • 105
  • 4
    Hmmm what if you want to copy the entire contents? I notied that `*` doesn't work – tread Jul 23 '16 at 07:29
  • Is there a way to use the copy module but specify that the copy is local to local? When I use copy module insted of command: cp it takes an absurd amount of time – agmezr Jun 21 '17 at 15:51
  • See the next post: https://stackoverflow.com/questions/40955440/how-to-copy-file-to-local-directory-using-ansible I'm not sure if it is what you are asking @agmezr – Asier Gomez Jun 23 '17 at 05:46
  • 1
    @AsierGomez after searching a lot I decided to use `shell: 'cp -r {{ source_dir }}/{{ item }} {{ deploy_dir }}'` it's a lot faster that using copy (maybe i'm doing something wrong) – agmezr Jun 23 '17 at 14:32
13

The simplest solution I've found to copy the contents of a folder without copying the folder itself is to use the following:

- name: Move directory contents
  command: cp -r /<source_path>/. /<dest_path>/

This resolves @surfer190's follow-up question:

Hmmm what if you want to copy the entire contents? I noticed that * doesn't work – surfer190 Jul 23 '16 at 7:29

* is a shell glob, in that it relies on your shell to enumerate all the files within the folder before running cp, while the . directly instructs cp to get the directory contents (see https://askubuntu.com/questions/86822/how-can-i-copy-the-contents-of-a-folder-to-another-folder-in-a-different-directo)

fractalic
  • 387
  • 2
  • 10
4

Ansible remote_src does not support recursive copying.See remote_src description in Ansible copy docs

To recursively copy the contents of a folder and to make sure the task stays idempotent I usually do it this way:

- name: get file names to copy
  command: "find /home/vagrant/dist -type f"
  register: files_to_copy

- name: copy files
  copy:
    src: "{{ item }}" 
    dest: "/usr/share/nginx/html"
    owner: nginx
    group: nginx
    remote_src: True
    mode: 0644
  with_items:
   - "{{ files_to_copy.stdout_lines }}"

Downside is that the find command still shows up as 'changed'

dvanrensburg
  • 1,351
  • 1
  • 14
  • 21
  • 4
    You can prevent the find command showing as 'changed' with changed_when: false – DMCoding Feb 09 '17 at 20:39
  • 2
    This is nonsensical. You specifically wrote "*to recursively copy the contents*", but your tasks flatten the structure and copy all files from subdirectories to a single directory. – techraf Nov 20 '17 at 22:23
4

the ansible doc is quite clear https://docs.ansible.com/ansible/latest/collections/ansible/builtin/copy_module.html for parameter src it says the following:

Local path to a file to copy to the remote server.
This can be absolute or relative.
If path is a directory, it is copied recursively. In this case, if path ends with "/", 
only inside contents of that directory are copied to destination. Otherwise, if it 
does not end with "/", the directory itself with all contents is copied. This behavior 
is similar to the rsync command line tool.

So what you need is skip the / at the end of your src path.

- name: copy html file
  copy: src=/home/vagrant/dist dest=/usr/share/nginx/html/
3

I found a workaround for recursive copying from remote to remote :

- name: List files in /usr/share/easy-rsa
  find:
    path: /usr/share/easy-rsa
    recurse: yes
    file_type: any
  register: find_result

- name: Create the directories
  file:
    path: "{{ item.path | regex_replace('/usr/share/easy-rsa','/etc/easy-rsa') }}"
    state: directory
    mode: "{{ item.mode }}"
  with_items:
    - "{{ find_result.files }}"
  when:
    - item.isdir

- name: Copy the files
  copy:
    src: "{{ item.path }}"
    dest: "{{ item.path | regex_replace('/usr/share/easy-rsa','/etc/easy-rsa') }}"
    remote_src: yes
    mode: "{{ item.mode }}"
  with_items:
    - "{{ find_result.files }}"
  when:
    - item.isdir == False
agrimal
  • 31
  • 1
2

I got involved whole a day, too! and finally found the solution in shell command instead of copy: or command: as below:

- hosts: remote-server-name
  gather_facts: no
  vars:
    src_path: "/path/to/source/"
    des_path: "/path/to/dest/"
  tasks:
  - name: Ansible copy files remote to remote
    shell: 'cp -r {{ src_path }}/. {{ des_path }}'

strictly notice to: 1. src_path and des_path end by / symbol 2. in shell command src_path ends by . which shows all content of directory 3. I used my remote-server-name both in hosts: and execute shell section of jenkins, instead of remote_src: specifier in playbook.

I guess it is a good advice to run below command in Execute Shell section in jenkins:

ansible-playbook  copy-payment.yml -i remote-server-name
foad322
  • 71
  • 10
1

Below worked for me,

-name: Upload html app directory to Deployment host
 copy: src=/var/lib/jenkins/workspace/Demoapp/html dest=/var/www/ directory_mode=yes
0

This I found an ideal solution for copying file from Ansible server to remote.

copying yaml file

- hosts: localhost
  user: {{ user }}
  connection: ssh
  become: yes
  gather_facts: no
  tasks:
   - name: Creation of directory on remote server
     file:
       path: /var/lib/jenkins/.aws
       state: directory
       mode: 0755
     register: result
   - debug: 
       var: result

   - name: get file names to copy
     command: "find conf/.aws -type f"
     register: files_to_copy

   - name: copy files
     copy:
      src: "{{ item }}" 
      dest: "/var/lib/jenkins/.aws"
      owner: {{ user }}
      group: {{ group }}
      remote_src: True
      mode: 0644
     with_items:
      - "{{ files_to_copy.stdout_lines }}"
Dilip
  • 2,622
  • 1
  • 20
  • 27
0

How to copy directory and sub dirs's and files from ansible server to remote host

- name: copy nmonchart39 directory  to {{ inventory_hostname }}
  copy:
    src: /home/ansib.usr.srv/automation/monitoring/nmonchart39
    dest: /var/nmon/data


Where:
copy entire directory: src: /automation/monitoring/nmonchart39
copy directory contents src: nmonchart39/
  • Please do not answer old questions (here:4 years ago) unless you have something important to add that the other answers fail at / are inefficient with. Your answer seems to be a duplicate of all the other ones – finnmglas Jul 21 '20 at 10:05