225
votes

How is it possible to move/rename a file/directory using an Ansible module on a remote system? I don't want to use the command/shell tasks and I don't want to copy the file from the local system to the remote system.

12
Why don't you want to use command/shell?Nick Urban
Just wanted to know if there is a way without using the mentioned tasks. Looks like there is no other way at the moment.Christian Berendt
Why do you want to move it specifically instead of copy it? That seems like a one-off action, rather than an idempotent ensuring-the-state-of-the-system type of step.Nick Urban
I have a sample configuration file included in a RPM package and I want to move this sample configuration file.Christian Berendt
At the moment I'm using a symlink to reference the file. Using get_url is no option for me because the system cannot reach the internet.Christian Berendt

12 Answers

228
votes

The file module doesn't copy files on the remote system. The src parameter is only used by the file module when creating a symlink to a file.

If you want to move/rename a file entirely on a remote system then your best bet is to use the command module to just invoke the appropriate command:

- name: Move foo to bar
  command: mv /path/to/foo /path/to/bar

If you want to get fancy then you could first use the stat module to check that foo actually exists:

- name: stat foo
  stat: path=/path/to/foo
  register: foo_stat

- name: Move foo to bar
  command: mv /path/to/foo /path/to/bar
  when: foo_stat.stat.exists
241
votes

From version 2.0, in copy module you can use remote_src parameter.

If True it will go to the remote/target machine for the src.

- name: Copy files from foo to bar
  copy: remote_src=True src=/path/to/foo dest=/path/to/bar

If you want to move file you need to delete old file with file module

- name: Remove old files foo
  file: path=/path/to/foo state=absent

From version 2.8 copy module remote_src supports recursive copying.

113
votes

I have found the creates option in the command module useful. How about this:

- name: Move foo to bar
  command: creates="path/to/bar" mv /path/to/foo /path/to/bar

I used to do a 2 task approach using stat like Bruce P suggests. Now I do this as one task with creates. I think this is a lot clearer.

10
votes

Another Option that has worked well for me is using the synchronize module . Then remove the original directory using the file module.

Here is an example from the docs:

- synchronize:
    src: /first/absolute/path
    dest: /second/absolute/path
    archive: yes
  delegate_to: "{{ inventory_hostname }}"
5
votes

This is the way I got it working for me:

  Tasks:
  - name: checking if the file 1 exists
     stat:      
      path: /path/to/foo abc.xts
     register: stat_result

  - name: moving file 1
    command: mv /path/to/foo abc.xts /tmp
    when: stat_result.stat.exists == True

the playbook above, will check if file abc.xts exists before move the file to tmp folder.

5
votes

Another way to achieve this is using file with state: hard.

This is an example I got to work:

- name: Link source file to another destination
  file:
    src: /path/to/source/file
    path: /target/path/of/file
    state: hard

Only tested on localhost (OSX) though, but should work on Linux as well. I can't tell for Windows.

Note that absolute paths are needed. Else it wouldn't let me create the link. Also you can't cross filesystems, so working with any mounted media might fail.

The hardlink is very similar to moving, if you remove the source file afterwards:

- name: Remove old file
  file:
    path: /path/to/source/file
    state: absent

Another benefit is that changes are persisted when you're in the middle of a play. So if someone changes the source, any change is reflected in the target file.

You can verify the number of links to a file via ls -l. The number of hardlinks is shown next to the mode (e.g. rwxr-xr-x 2, when a file has 2 links).

4
votes

Bruce wasn't attempting to stat the destination to check whether or not to move the file if it was already there; he was making sure the file to be moved actually existed before attempting the mv.

If your interest, like Tom's, is to only move if the file doesn't already exist, I think we should still integrate Bruce's check into the mix:

- name: stat foo
  stat: path=/path/to/foo
  register: foo_stat

- name: Move foo to bar
  command: creates="path/to/bar" mv /path/to/foo /path/to/bar
  when: foo_stat.stat.exists
4
votes

I know it's a YEARS old topic, but I got frustrated and built a role for myself to do exactly this for an arbitrary list of files. Extend as you see fit:

main.yml

- name: created destination directory
  file:
    path: /path/to/directory
    state: directory
    mode: '0750'
- include_tasks: move.yml
  loop:
    - file1
    - file2
    - file3

move.yml

- name: stat the file
  stat:
    path: {{ item }}
  register: my_file

- name: hard link the file into directory
  file:
    src: /original/path/to/{{ item }}
    dest: /path/to/directory/{{ item }}
    state: hard
  when: my_file.stat.exists

- name: Delete the original file
  file:
    path: /original/path/to/{{ item }}
    state: absent
  when: my_file.stat.exists

Note that hard linking is preferable to copying here, because it inherently preserves ownership and permissions (in addition to not consuming more disk space for a second copy of the file).

4
votes
- name: Move the src file to dest
  command: mv /path/to/src /path/to/dest
  args:
    removes: /path/to/src
    creates: /path/to/dest

This runs the mv command only when /path/to/src exists and /path/to/dest does not, so it runs once per host, moves the file, then doesn't run again.

I use this method when I need to move a file or directory on several hundred hosts, many of which may be powered off at any given time. It's idempotent and safe to leave in a playbook.

1
votes

This may seem like overkill, but if you want to avoid using the command module (which I do, because it using command is not idempotent) you can use a combination of copy and unarchive.

  1. Use tar to archive the file(s) you will need. If you think ahead this actually makes sense. You may want a series of files in a given directory. Create that directory with all of the files and archive them in a tar.
  2. Use the unarchive module. When you do that, along with the destination: and remote_src: keyword, you can place copy all of your files to a temporary folder to start with and then unpack them exactly where you want to.
1
votes

On Windows: - name: Move old folder to backup win_command: "cmd.exe /c move /Y {{ sourcePath }} {{ destinationFolderPath }}"

To rename use rename or ren command instead

0
votes

You can Do It by --

Using Ad Hoc Command

ansible all -m command -a" mv /path/to/foo /path/to/bar"

Or You if you want to do it by using playbook

- name: Move File foo to destination bar
  command: mv /path/to/foo /path/to/bar