2

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?

β.εηοιτ.βε
  • 33,893
  • 13
  • 69
  • 83
Stephan Kristyn
  • 15,015
  • 14
  • 88
  • 147
  • 2
    are you aware of the [`| default(omit)` filter](https://docs.ansible.com/ansible/2.10/user_guide/playbooks_filters.html#making-variables-optional)? You're running uphill trying to outsmart a simple `when:` condition, but in the spirit of answering what was asked ... – mdaniel Mar 16 '21 at 20:09
  • 2
    And I guess in your second example you meant `content: "{{ file_cert_variable }}"` – β.εηοιτ.βε Mar 16 '21 at 20:21
  • Corrected the example. The default filter is neat, but wont sovle the problem that content can't read files and src can't read content. when condition is not an option, because I have maybe 50 cases of this. And if-else should be avoided as often as possible in any modern programming paradigma in my opinion. I think lookup combined with default filter should work. – Stephan Kristyn Mar 16 '21 at 21:06
  • 2
    @SteveK from your question the real use case is not super clear, tbh. Could you elaborate on both cases: what would you get and how when you have to drop a certificate as a file and same for when you get it as a string. What I would expect is `src: "{{ path_to_cert | default(omit) }}"` and `content: "{{ file_cert_variable | default(omit) }}"` in the same task. Then based on what the inventory or Tower stands, you'd have either a `path_to_cert` variable defined or a `file_cert_variable` variable defined, ending in the `omit` working as it should. – β.εηοιτ.βε Mar 16 '21 at 21:44
  • 1
    And, yes, a lookup like: `content: "{{ lookup('file', 'folder/file.cert') }}"` could probably do... but then you'll still need to resort to some sort of `content: "{{ file_cert_variable if file_cert_variable is defined else lookup('file', 'folder/file.cert') }}"`, so you end up with quite the same as a `when`, aren't you? – β.εηοιτ.βε Mar 16 '21 at 21:57

1 Answers1

2

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 }}"

Zeitounator
  • 38,476
  • 7
  • 53
  • 66