14

When I execute an ansible module with a loop that has dictionaries with values to be used by the module, including confidential information, I can hide it using loop_control.label, but not when an error happens.

- ansible.module: arg1: "{{ item.name }}" arg2: "{{ item.some_value }}" arg3: "{{ item.secret }}" arg4: "{{ item.throw_error }}" loop: - name: "item1" some_value: "my value" secret: "p4$$w0rd" loop_control: label: "{{ item.name }}" 

I can hide it using no_log: true, but it then hides everything, including when a sucess happens, as well as the error message, that could be perfectly fine, and making me clueless about the actual error when I don't see it.

This is similar to stop all logs in a reverse proxy, or log everything, including authorization headers, both of which are far from desired in a production environment.

So, is there a way to make it not log the loop items when an error occurs, but log everything else? It could be either a task flag, just like no_log, or some global configuration, to include in the ansible.cfg file.

Update (2021-04-19)

The answer from Vladimir actually works as a workaround, but there are several downsides in using it:

  1. You have to install the library jmespath prior to running json_query filter.
  2. It has more code and make the code as a whole less readable.
  3. If you have lots of loops (my specific project has over 80 of them), the code can become really ugly (also because of point 2).
  1. If you have a loop in a parent file, you should use loop_control with loop_var in the debug loop.
  2. The error and where it shows the error message will be different places. The same applies for success messages.
  3. The loop label in the debug task will be different than the one in the main loop task (which may make it more difficult to associate success and error cases to the actual items).
  4. The 2 tasks will be printed unnecessarily when everything is fine (with success messages printed separately from the actual tasks, making the output worse when everything runs successfully, which should be most cases).
  5. Changing and testing the existing loop tasks, as well as making any new loops follow this format, will be a maintenance burden (especially because of point 2, that makes me think if I'm actually improving the code, or making it worse).

(and there are probably more downsides)

So the best I could say, is that this is a workaround that solves a problem and creates several others.

That said, I asked for a solution, and based on how Ansible works at the moment, the provided workaround (or some similar workaround) seems to be the only way to achieve what I asked.

I will weigh the pros and cons to see if I will implement this workaround, or if I continue to use it the way it is now.

2 Answers 2

6

There is a way to achieve desired behavior in multuple tasks with ansible's error handling. You can register output of no_log task and print only non-secret part in subsequent debug:

- hosts: localhost connection: local tasks: - block: - shell: "/bin/false" loop: - name: "item1" some_value: "my value" secret: "p4$$w0rd" - name: "item2" some_value: "my value" secret: "p4$$w0rd" loop_control: label: "{{ item.name }}" # Capture tasks output register: my_task no_log: true # Run this if task above fails rescue: # Print msg, stderr or whatever needed - debug: msg: "{{ item }}" loop: "{{ my_task | json_query('results[*].msg') }}" # finally fail a play - fail: 

In case if you need always print output (not only on task failure) use always instead of rescue and fail task on condition:

- hosts: localhost connection: local tasks: - block: - shell: "/bin/true" loop: - name: "item1" some_value: "my value" secret: "p4$$w0rd" - name: "item2" some_value: "my value" secret: "p4$$w0rd" loop_control: label: "{{ item.name }}" register: my_task no_log: true always: - debug: msg: "{{ item }}" loop: "{{ my_task | json_query('results[*].msg') }}" - fail: when: my_task['failed'] | default(false) 

Update (2021-04-20)

As pointed by Lucas code above have number of downsides. Main idea was that output can be registered and filtered afterwards. Other parts of code are opinionated examples. There is certainly room for improvement. For example here is code that addresses issues 1, 4, 6, (and partially 2):

- hosts: localhost connection: local tasks: - block: - shell: "/bin/true" register: my_task no_log: true # Register all loop-related keys so same loop settings can be reused in debug <<: &loop loop: - name: "item1" some_value: "my value" secret: "p4$$w0rd" - name: "item2" some_value: "my value" secret: "p4$$w0rd" loop_control: label: "{{ item.name }}" index_var: index always: - debug: # match result with item by index msg: "{{ my_task['results'][index]['msg'] | default('ok') }}" # reuse loop from main task <<: *loop - fail: when: my_task['failed'] | default(false) 

As for now it seems there is no way to implement desired behavior w/o workarounds. Ansible recommends writing logs to secure location in case it contains sensitive data.

2
  • Thanks for the answer. I accepted it, as it seems the only way I can achieve what I asked is to use the provided workaround (or some similar workaround), although I edited my question to point several downsides of using this approach, especially in bigger projects with lots of tasks with loops. I will weigh the pros and cons to see if I will implement this workaround, or if I continue to use it the way it is now, but thanks for taking your time to post this answer. Commented Apr 19, 2021 at 14:19
  • Thanks for the update, that solves points 1 and 6, and helps to minimize the downsides of points 2, 3, 4 and 5, although it's important to make sure that the debug task never errors, otherwise the items will show. But it's important to take care about the anchors in files with more than 1 loop, and especially when moving loops through files, when changing the code, to make sure each loop has its own uniquely named anchor. Commented Apr 22, 2021 at 20:01
2

You can control the label of each item that Ansible prints with loop_control. For example:

- name: Task loop: {{ data | dict2items }} loop_control: label: "{{ item.key }}" ansible.builtin.debug: msg: "{{ item }}" 

That will print just the key of each item instead of the potentially large or sensitive value of each item.

2
  • 1
    With only this approach, sensitive data could still leak, such as when an error occurs and an error message is printed, or if verbosity is increased (depends on how the data is used). Commented Apr 3 at 12:24
  • Further more, this is what OP is already doing. This answer adds nothing new. Commented Oct 14 at 8:13

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.