Introduction
Ansible has no dedicated "rename" module. Instead, use ansible.builtin.copy + ansible.builtin.file (idempotent), or ansible.builtin.command with mv (simple). This guide covers all approaches with their trade-offs.
Method 1: copy + file (Idempotent)
# Rename by copying to new name, then removing original
- name: Copy file to new name
ansible.builtin.copy:
src: /opt/myapp/config.old
dest: /opt/myapp/config.new
remote_src: true
- name: Remove original
ansible.builtin.file:
path: /opt/myapp/config.old
state: absent
Pros: Idempotent, safe, preserves permissions Cons: Two tasks, briefly duplicates data
Method 2: command mv (Simple)
# Simple rename
- name: Rename file
ansible.builtin.command: mv /opt/myapp/old_name.conf /opt/myapp/new_name.conf
args:
removes: /opt/myapp/old_name.conf
creates: /opt/myapp/new_name.conf
removes + creates make it idempotent:
- Skips if source doesn't exist (already renamed)
- Skips if destination already exists
# Rename directory
- name: Rename directory
ansible.builtin.command: mv /opt/old_app /opt/new_app
args:
removes: /opt/old_app
creates: /opt/new_app
Pros: Simple, atomic, handles directories Cons: Uses command module (ansible-lint warning)
Method 3: stat + command (Safe)
# Check before rename
- name: Check if old file exists
ansible.builtin.stat:
path: /opt/myapp/old_name.conf
register: old_file
- name: Rename file
ansible.builtin.command: mv /opt/myapp/old_name.conf /opt/myapp/new_name.conf
when: old_file.stat.exists
Rename Multiple Files
# Rename with pattern
- name: Rename config files
ansible.builtin.command: "mv {{ item.old }} {{ item.new }}"
args:
removes: "{{ item.old }}"
creates: "{{ item.new }}"
loop:
- { old: /etc/myapp/app.conf.bak, new: /etc/myapp/app.conf }
- { old: /etc/myapp/db.conf.bak, new: /etc/myapp/db.conf }
- { old: /etc/myapp/log.conf.bak, new: /etc/myapp/log.conf }
Rename with Backup
# Backup current, then rename new
- name: Backup current config
ansible.builtin.copy:
src: /etc/myapp/config.yml
dest: "/etc/myapp/config.yml.{{ ansible_date_time.date }}.bak"
remote_src: true
ignore_errors: true # OK if no current config
- name: Move new config into place
ansible.builtin.command: mv /tmp/config.yml.new /etc/myapp/config.yml
args:
removes: /tmp/config.yml.new
Rename Directory
# Rename directory
- name: Rename app directory
ansible.builtin.command: mv /opt/myapp-v1 /opt/myapp-v2
args:
removes: /opt/myapp-v1
creates: /opt/myapp-v2
# Rename with contents preservation
- name: Rename and update symlink
block:
- name: Rename directory
ansible.builtin.command: mv /opt/myapp-v1 /opt/myapp-v2
args:
removes: /opt/myapp-v1
creates: /opt/myapp-v2
- name: Update symlink
ansible.builtin.file:
src: /opt/myapp-v2
dest: /opt/myapp
state: link
Windows Rename
# Windows file rename
- name: Rename file on Windows
ansible.windows.win_command: >
cmd /c move "C:\app\old_name.txt" "C:\app\new_name.txt"
args:
creates: "C:\\app\\new_name.txt"
removes: "C:\\app\\old_name.txt"
# PowerShell rename
- name: Rename with PowerShell
ansible.windows.win_powershell:
script: |
if (Test-Path "C:\app\old_name.txt") {
Rename-Item -Path "C:\app\old_name.txt" -NewName "new_name.txt"
}
Atomic Rename Pattern
For configuration files that services read:
# Write to temp, then atomic rename
- name: Generate config to temp file
ansible.builtin.template:
src: config.yml.j2
dest: /etc/myapp/config.yml.tmp
validate: "/opt/myapp/validate-config %s"
- name: Atomic rename into place
ansible.builtin.command: mv /etc/myapp/config.yml.tmp /etc/myapp/config.yml
args:
removes: /etc/myapp/config.yml.tmp
notify: Reload myapp
Batch Rename with find
# Find and rename files matching pattern
- name: Find .bak files
ansible.builtin.find:
paths: /etc/myapp/
patterns: "*.bak"
register: bak_files
- name: Rename .bak to .conf
ansible.builtin.command: "mv {{ item.path }} {{ item.path | regex_replace('\\.bak$', '.conf') }}"
loop: "{{ bak_files.files }}"
Troubleshooting
"mv: cannot move — Permission denied"
- command: mv /etc/protected.conf /etc/protected.conf.old
become: true
Cross-Filesystem Move
mv across filesystems copies then deletes. For large files:
- ansible.posix.synchronize:
src: /mount1/bigfile
dest: /mount2/bigfile
delegate_to: "{{ inventory_hostname }}"
- ansible.builtin.file:
path: /mount1/bigfile
state: absent
Related Articles
Conclusion
For simple renames, use command: mv with removes/creates for idempotency. For safety-critical renames, use copy + file (two tasks but fully idempotent). For atomic config updates, write to .tmp then mv into place. Always check with stat if unsure whether the source exists. On Windows, use win_command or win_powershell with Rename-Item.