Copy local file if exists, using ansible
Asked Answered
P

7

33

I'm working in a project, and we use ansible to create a deploy a cluster of servers. One of the tasks that I've to implement, is to copy a local file to the remote host, only if that file exists locally. Now I'm trying to solve this problem using this

- hosts: 127.0.0.1 
  connection: local
  tasks:
    - name: copy local filetocopy.zip to remote if exists
    - shell: if [[ -f "../filetocopy.zip" ]]; then /bin/true; else /bin/false; fi;
      register: result    
    - copy: src=../filetocopy.zip dest=/tmp/filetocopy.zip
      when: result|success

Bu this is failing with the following message: ERROR: 'action' or 'local_action' attribute missing in task "copy local filetocopy.zip to remote if exists"

I've tried to create this if with command task. I've already tried to create this task with a local_action, but I couldn't make it work. All samples that I've found, doesn't consider a shell into local_action, there are only samples of command, and neither of them have anything else then a command. Is there a way to do this task using ansible?

Proof answered 4/3, 2015 at 13:3 Comment(2)
Does this answer your question? Ansible include task only if file existsRegressive
Hi @HelderPereira this was posted sometime ago, at that time the accepted answer solved my problem. The most voted one also solved this problem at that time, I can't evaluate it anymore, because I don't have access to it's code.Proof
T
24

Change your first step into the following on

- name: copy local filetocopy.zip to remote if exists
  local_action: stat path="../filetocopy.zip"
  register: result    
Tidings answered 4/3, 2015 at 14:9 Comment(10)
Hi @sandra-parsick , thanks for the reply, this is still failing with < TASK: copy local filetocopy.zip to remote if exists > failed: [52.11.12.29 -> 127.0.0.1] => {"failed": true, "parsed": false} [sudo via ansible, key=] password:Proof
it seems, it doesn't like a sudo. How are you calling the playbook?Tidings
the standard pattern for this is the stat module, which avoids the need for a shell/command.Harlow
@SandraParsick I'm calling it like this: ansible-playbook -i inventory/hosts my.ymlProof
@tedder42 You mean to use stat to know if the file exists?Proof
yes, I mean use stat for existence. here's an example.Harlow
@tedder42 you are right. My main point was that he has to use the local action command for running the check on the local machine. I corrected the stepTidings
You also want "ignore_errors: true" and "changed_when: false" in the "register" stanza.Bacciform
In the current version of Ansible, I had to add become: false to get this to work. Otherwise, it gives an error about not being able to sudo and fails.Turbary
Thanks @Turbary this worked for me. Odd to me that Ansible seemingly defaults to attempting sudo for local tasks 🤔Corr
S
37

A more comprehensive answer:

If you want to check the existence of a local file before performing some task, here is the comprehensive snippet:

- name: get file stat to be able to perform a check in the following task
  local_action: stat path=/path/to/file
  register: file

- name: copy file if it exists
  copy: src=/path/to/file dest=/destination/path
  when: file.stat.exists

If you want to check the existence of a remote file before performing some task, this is the way to go:

- name: get file stat to be able to perform check in the following task
  stat: path=/path/to/file
  register: file

- name: copy file if it exists
  copy: src=/path/to/file dest=/destination/path
  when: file.stat.exists
Susansusana answered 29/10, 2015 at 9:12 Comment(0)
T
24

Change your first step into the following on

- name: copy local filetocopy.zip to remote if exists
  local_action: stat path="../filetocopy.zip"
  register: result    
Tidings answered 4/3, 2015 at 14:9 Comment(10)
Hi @sandra-parsick , thanks for the reply, this is still failing with < TASK: copy local filetocopy.zip to remote if exists > failed: [52.11.12.29 -> 127.0.0.1] => {"failed": true, "parsed": false} [sudo via ansible, key=] password:Proof
it seems, it doesn't like a sudo. How are you calling the playbook?Tidings
the standard pattern for this is the stat module, which avoids the need for a shell/command.Harlow
@SandraParsick I'm calling it like this: ansible-playbook -i inventory/hosts my.ymlProof
@tedder42 You mean to use stat to know if the file exists?Proof
yes, I mean use stat for existence. here's an example.Harlow
@tedder42 you are right. My main point was that he has to use the local action command for running the check on the local machine. I corrected the stepTidings
You also want "ignore_errors: true" and "changed_when: false" in the "register" stanza.Bacciform
In the current version of Ansible, I had to add become: false to get this to work. Otherwise, it gives an error about not being able to sudo and fails.Turbary
Thanks @Turbary this worked for me. Odd to me that Ansible seemingly defaults to attempting sudo for local tasks 🤔Corr
S
24

If you don't wont to set up two tasks, you could use 'is file' to check if local files exists:

tasks:
- copy: src=/a/b/filetocopy.zip dest=/tmp/filetocopy.zip
  when: '/a/b/filetocopy.zip' is file

The path is relative to the playbook directory, so using the magic variable role_path is recommended if you are referring to files inside the role directory.

Ref: http://docs.ansible.com/ansible/latest/playbooks_tests.html#testing-paths

Shindig answered 11/10, 2017 at 23:46 Comment(2)
This is clearly the most declarative version and it can be used in any tasks without requiring additional tasks. In recent versions of Ansible (2.6+) it can be written as when: '/a/b/filetocopy.zip' is file as can be seen from the URL that you provided for the documentation.Ramification
trying to write this in old version and getting error This one looks easy to fix. It seems that there is a value started with a quote, and the YAML parser is expecting to see the line ended with the same kind of quote. For instance: when: "ok" in result.stdout Could be written as: when: '"ok" in result.stdout' or equivalently: when: "'ok' in result.stdout"Etheline
W
4

Fileglob permits a lookup of an eventually present file.

- name: copy file if it exists
  copy: src="{{ item }}" dest=/destination/path
  with_fileglob: "/path/to/file"
Wellmeaning answered 5/5, 2017 at 7:28 Comment(0)
R
1

How about this?

tasks:
- copy: src=../filetocopy.zip dest=/tmp/filetocopy.zip
  failed_when: false

This will copy the file to the target if it exists locally. If it does not exists, it simply does nothing since the error is ignored.

Rawlins answered 26/2, 2016 at 16:18 Comment(4)
Is this a comment or an answer?Independent
An answer, I added some explanation.Rawlins
Interesting, I didn't have time to test it but seems greatProof
This will hide other failure cases like running out of disk space.Elsi
G
1

Apologies for resurrecting the dead, this was the first result of my search.

Use the 'file' lookup to try and load the file contents of local files - doesn't work for remote files.

- name: "Use lookup to test for local_file"
  vars:
    local_file: "{{ playbook_dir }}/generated.file"
    remote_file: "~/remote.file"
  when: lookup( 'file', local_file, errors='ignore' )
  copy:
    src: "{{ local_file}}"
    dest: "{{ remote_file }}"

Or use it for defaults:

- name: "Use lookup to test for local_file or use a default_file"
  vars:
    local_file: "{{ playbook_dir }}/generated.file"
    default_file: "{{ playbook_dir }}/default.file"
    file_to_copy: "{{ local_file if lookup( 'file', local_file, errors='ignore' else default_file )
  copy:
    src: "{{ file_to_copy}}"
    dest: "{{ remote_file }}"

NOTE: Small caveat for Ansible 2.9 is the warning message, even though it's supposed to be 'ignored' - I'd expect this if using the 'warning' option.

Gathard answered 9/9, 2022 at 11:7 Comment(0)
M
0

Small correction to one of the examples by @awltux, the syntax should be:

- name: "Use lookup to test for local_file or use a default_file"
  vars:
    local_file: "{{ playbook_dir }}/generated.file"
    default_file: "{{ playbook_dir }}/default.file"
    file_to_copy: "{{ local_file if lookup( 'file', local_file, errors='ignore') else default_file }}"
  copy:
    src: "{{ file_to_copy}}"
    dest: "{{ remote_file }}"
Makell answered 7/3 at 22:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.