How to Copy Files and Directories in Ansible Using Copy and Fetch Modules

Ansible provides the basic functionality of copying files and directories through the copy and fetch modules.

You can use the copy module to copy files and folders from the local server to the remote servers, between remote servers(only files), change the permission of the files, etc.

You can use the fetch module to copy files from the remote machine to the local machine.

If you need to copy files after substituting with variables, like config files with IP changes, use template module instead.

Copying files from a local machine to the remote server

By default, the copy module will check the file set in the src parameter, on the local machine. And then it will copy the file to the remote machine path specified in the dest path. The example below will copy the sample.txt file in the home directory of the current user( on the local machine), to the /tmp directory on the remote server. Since we are not specifying any permission for the file, the default permission for the remote file is set as -rw-rw-r–(0664).

- hosts: blocks
  tasks:
  - name: Ansible copy file to remote server
    copy:
      src: ~/sample.txt
      dest: /tmp 

Note 1: If the file is already present on the remote server and if the source file’s content is different, then on running the task, the destination file will be modified. You can control this by setting the force parameter. The default is set to yes’. So it modifies the file by default. If you don’t want the file to be modified if the source file is different, then you can set it to ‘No’. The following task will only copy the file if the file does not exist on the remote server.

- hosts: blocks
  tasks:
  - name: Ansible copy file force
    copy:
      src: ~/sample.txt
      dest: /tmp 
      force: no

Note 2: If the file couldn’t be found on the local machine, then Ansible will throw an error similar to below.
fatal: [remote-machine-1]: FAILED! => {“changed”: false, “failed”: true, “msg”: “Unable to find ‘~/sample.txt’ in expected paths.”}

Copying a directory from a local machine to the remote server

You can also copy folders/directories using Ansible copy module. If the ‘src’ path is a directory, it will be copied recursively. That means the entire directory is copied.

Now, there are two different variations for this. Depending on whether you have the ‘/’ character at the end of the ‘src’ path or not.

The first method will create a directory on the remote server, with the name set in the src parameter. And then it will copy and paste the contents of the source folder to that directory. If you want this behaviour, then dont give the ‘/’ after the path in the src parameter.

The below Ansible copy directory example will first create a directory named  copy_dir_ex in the /tmp of the remote server. See how there is a ‘copy_dir_ex’ folder inside the ‘tmp’ folder. 

- hosts: blocks
  tasks:
  - name: Ansible copy directory to the remote server
    copy:
      src:/Users/mdtutorials2/Documents/Ansible/copy_dir_ex
      dest:/Users/mdtutorials2/Documents/Ansible/tmp

output
------
Ansible-Pro:Ansible mdtutorials2$ tree tmp
tmp
└── copy_dir_ex
    ├── file1
    ├── file2
    ├── file3
    └── tmp2
        ├── file4
        └── file5

The second method will copy only the files from the source directory to the remote server. It will not create a directory on the remote server. If you want this behaviour, then give the ‘/’ after the path in the src parameter.

In the below example the files inside the copy_dir_ex will be copied to the /tmp folder of the remote server. As you can see the src directory is not created in the destination. Only the contents of the directory are copied.

- hosts: blocks
  tasks:
  - name: Ansible copy files from a directory to remote server
    copy:
      src:/Users/mdtutorials2/Documents/Ansible/copy_dir_ex/
      dest:/Users/mdtutorials2/Documents/Ansible/tmp

output
------
tmp/
├── file1
├── file2
├── file3
└── tmp2
    ├── file4
    └── file5

Note 1: If we need to set the permissions of the remote directory we can do it using the directory_mode parameter. The permissions will be set only if the directory does not exist on the remote machine.

Note 2: You can also set the group and owner of the directory. You should set the respective names to the ‘group’ and ‘owner’ parameters.

Copying files between different folders on the same remote machine

You can also copy files between the various locations on the remote servers. You have to set the remote_src parameter to yes.

The following example copies the hello6 file in the /tmp directory of the remote server and pastes it in the /etc/ directory.

- hosts: blocks
  tasks:
  - name: Ansible copy files remote to remote
    copy:
      src: /tmp/hello6
      dest: /etc
      remote_src: yes

Note 1: As of Ansible 2.2.1.0, copying directories in the remote servers are not supported. If you try, you will get the following error.

fatal: [remote-machine-1]: FAILED! => {"changed": false, "failed": true, "msg": "Remote copy does not support recursive copy of directory: /tmp/copy_dir_ex"}

Copying multiple files/directories using with_items

If you have multiple files to be copied, then you can iterate over them using with_items.

The following example will copy multiple files given as a list of the home directory.

- hosts: blocks
  tasks:
  - name: Ansible copy multiple files with_items
    copy:
      src: ~/{{item}}
      dest: /tmp
      mode: 0774
    with_items:
      ['hello1','hello2','hello3','sub_folder/hello4']

Copying multiple files with different permission/destination settings

In the above task, we are copying multiple files, but all the files have the same permissions and same destination. But sometimes we would want to set permissions for different files or maybe the destination folder will be different for each file. This is possible by using with_items along with dictionary structure.

In the following task, I am trying to copy 3 files to 2 different folders. Also, the file permissions are different for each of them. I am giving a dictionary structure mentioning the different setting for each file.

As you can see from the output the files are copied to the given folders and the permissions are set correctly.

- hosts: all
  tasks:
  - name: Copy multiple files in Ansible with different permissions
    copy:
      src: "{{ item.src }}"
      dest: "{{ item.dest }}"
      mode: "{{item.mode}}"
    with_items:
      - { src: '/home/mdtutorials2/test1',dest: '/tmp/devops_system1', mode: '0777'}
      - { src: '/home/mdtutorials2/test2',dest: '/tmp/devops_system2', mode: '0707'}
      - { src: '/home/mdtutorials2/test3',dest: '/tmp2/devops_system3', mode: '0575'}

output
======
mdtutorials2@system01:~$ ls -lrt /tmp
drwxrwxrwx 2 root          root          4096 Oct  9 14:28 devops_system1
drwx---rwx 2 root          root          4096 Oct  9 14:28 devops_system2
mdtutorials2@system01:~$ ls -lrt /tmp2
-r-xrwxr-x 1 root root 0 Oct  9 14:33 devops_system3

Copying all the files inside a folder that matches a pattern(wildcard)

If you need to copy all the files in a directory that matches a wildcard character, then you can use with_fileglob.

In the following example, all files that start with ‘hello’ in the /tmp directory of the local machine is copied to the remote server.

- hosts: blocks
  tasks:
  - name: Ansible copy multiple files with wildcard matching.
    copy:
      src: "{{ item }}"
      dest: /etc
    with_fileglob:
      - /tmp/hello*

Creating backup of a file in the remote servers before copy

While copying files, mistakes can happen. You may copy the wrong file, write wrong contents etc. This is going to create a lot of headaches. So it would be helpful if a backup of the remote file is created on the remote server.

Ansible copy module provides a ‘backup’ parameter just for that. If the remote file exists and if it is different from the file which is copied, then a new file will be created. The new file will be named by appending the timestamp and the original file name. The default value is ‘no’ for the backup parameter.

For example, the following example will create a backup of the helloworld.txt in the /tmp directory of the remote server. It will be named something like ‘helloworld.txt.8925.2017-04-05@16:53:13’.

- hosts: blocks
  tasks:
  - name: ansible copy file backup example
    copy:
      src: ~/helloworld.txt
      dest: /tmp
      backup: yes

Copying files using the Ad-hoc method

Most of the above tasks can be done in an Adhoc way also.

ansible blocks -m copy -a "src=~/sample.txt dest=/tmp" -s -i inventory.ini
ansible blocks -m copy -a "src=~/copy_dir_ex dest=/tmp" -s -i inventory.ini
ansible blocks -m copy -a "src=/tmp/hello6 dest=/tmp/hello7 remote_src=yes" -s -i inventory.ini

Copying files from remote machine to the local machine

You can also copy the files from remote servers to the local machine. This can be done using the Ansible fetch module. This is useful when you want to copy some log files from the remote servers to your local machine.

By default, a directory named after each host you are connecting will be created in your destination directory(local machine). The fetched files will be copied there. If the file doesn’t exist on the remote server, no error will be thrown by default.

In the following example, I am running the task on remote-server-1. The file will be copied into /etc/remote-server-1/tmp directory of the local machine.

- hosts: blocks
  tasks:
  - name: Ansible fetch files from remote server to the local machine using Ansible fetch module
    fetch:
      src: /tmp/hello2
      dest: /etc
      mode: 0774

If you don’t want this behavior and you need the file to be copied directly to the destination directory, then you should use the ‘flat’ parameter.

- hosts: blocks
  tasks:
  - name: Ansible fetch directory example with flat parameter set
    fetch:
      src: /tmp/hello2
      dest: /tmp/
      mode: 0774
      flat: yes

Note 1: If you use flat parameter and the file name is not unique the existing file will be replaced every time the file is fetched.

Note 2: If you want an error to be thrown if the source file is missing, then set the ‘fail_on_missing’ parameter to yes. The following example will throw an error if the remote file does not exist.

- hosts: blocks
  tasks:
  - name: Ansible fetch example with fail_on_missing set
    fetch:
      src: /tmp/fetch.txt
      dest: /tmp/
      mode: 0774
      fail_on_missing: yes

Note 1: If you are trying to set the destination path to a directory, put a ‘\’ at the end of the path. Else Ansible will run the task as if the destination path is a file and try to replace it. You might get the following error.

fatal: [remote-machine-1]: FAILED! => {“failed”: true, “msg”: “Failed to fetch the file: [Errno 21] Is a directory: ‘/tmp'”}

Writing to a file using the copy module

You can also write to a file using the contents parameter in Ansible copy module. The following example will write the values given to the content parameter to the check4.txt file.

- hosts: all
  tasks:
  - name: Ansible write to a file example
  - copy:
      content: |
        Content parameter example.
        Check4.txt will be created after this task is executed.
      dest: /Users/mdtutorials2/Documents/Ansible/check4.txt
      backup: yes

 

Return values of copy module

The copy module returns some values for each task. The full list is available in the Ansible docs.

Example:

    "changed": true, 
    "checksum": "98d8fb24e8b2c2cec9c5ae963bd65c3657f50b16", 
    "dest": "/tmp/sample.txt", 
    "gid": 0, 
    "group": "root", 
    "md5sum": "ce83d23d6eb6bf079e1fc5c448ea9a9f", 
    "mode": "0644", 
    "owner": "root", 
    "size": 13, 
    "src": "/home/mdtutorials2/.ansible/tmp/ansible-tmp-1489974916.02-178756727263160/source", 
    "state": "file", 
    "uid": 0