Copy module "content" works only for variables and "src" only for files, how to get it to work for both?

Viewed 540

I am using Ansible copy module as follows:

src: folder/file.cert
dest: file_dest.cert

Above works fine for files. But if I want to use Ansible Tower for certain inventories, I will provide file.cert as a variable.
Now src is not working anymore and I need to use the following:

content: file_cert_variable
dest: file_dest.cert

So far so good. But now I want to combine both worlds, so that I don't have to add a conditional in case we use Tower variables for some environments and, in other environments, files as source for certificates.
Is there a way to cover both cases with a single Ansible command?

1 Answers

The actual "ansiblish" way to describe the state of your remote for which you will find quite a few similar examples in existing roles is to make one action depending on the var existence or not:

- name: Copy certificate from variable if set
  copy:
    content: "{{ file_cert_variable }}"
    dest: file_dest.cert
  when: file_cert_variable is defined

- name: Copy certificate from local file when no variable is provided
  copy:
    src: folder/file.cert
    dest: file_dest.cert
  when: file_cert_variable is not defined

There are ways to 'shorten' this to a single copy task if you really can't stand 'repeating'. But you will still have to make a decision depending on the presence of the content var. In the end, you will not really have any performance advantage and the solutions might end up being less readable, harder to maintain and even more verbose in some cases. Here are two examples I could think of. Note that the second one is considered a bad practice and will fire a warning (i.e. Using a variable for a task's 'args' is unsafe in some situations). Also note that both are partial playbook extracts, written on spot and not individually tested.

Take 1: double omit

- name: define our source name when no var is provided
  # Note: defining the following var elsewhere will break this example
  set_fact:
    source_file: folder/file.cert
  when: file_cert_variable is not defined

- name: copy file/content to destination
  copy:
    src: "{{ source_file | default(omit) }}"
    content: "{{ file_cert_variable | default(omit) }}"
    dest: file_dest.cert

Take2: dynamic task dict

vars:
  copy_params_defaults:
    dest: file_dest.cert

  copy_params_additional:
    from_var:
      content: "{{ file_cert_variable | default('') }}"
    from_file:
      src: folder/file.cert

  copy_from: "{{ file_cert_variable is defined | ternary('from_var', 'from_file') }}"

  copy_params: "{{ copy_params_defaults | combine(copy_params_additional[copy_from]) }}"

tasks:
  - name: "Copy certificate {{ copy_from }}"
    copy: "{{ copy_params }}"

Related