With some file types, simple unified diff is not very helpful. One obvious example is binary files, but even certain ostensibly textual ones -- such as SSL-certificates (.pem) -- fall into this category too.

So, can a task specify its own method of displaying differences? Something like:

- name: Update SSL certificate
    src: etc/ssl/mycert.pem
    dest: /etc/ssl/mycert.pem
    exec: >
      openssl x509 -in {{ old }} -noout -text > {{ old }}.txt
      openssl x509 -in {{ new }} -noout -text > {{ new }}.txt
      diff -U2 {{ old }}.txt {{ new }}.txt
      rm -f {{ old }}.txt {{ new }}.txt


@konstantin-suvorov's "hack" is wonderful, but it seems like it would only work for modules, which already have a diff-method. Unfortunately, my other use-case involves a module (command), which does not.

In particular, I invoke /usr/bin/apt-get update on Ubuntu, which updates apt's cache-files. I'd like to dump the cache's content in text form before and after to see, what the update has changed -- if anything...


There's no out-of-the box solution. I was curious to challenge this.

Here's custom stdout callback (place into ./callback_plugins/diffhack.py).
It is based on default stdout callback default.py.

from ansible.plugins.callback.default import CallbackModule as DefaultCallback
from subprocess import check_output, CalledProcessError, STDOUT
from tempfile import mkstemp
import os

    from __main__ import display
except ImportError:
    display = None

class CallbackModule(DefaultCallback):

    def v2_on_file_diff(self, result):
        def process_diff(diff, cmd):
            for d in diff:
                for ab in ('after','before'):
                    fd, fn = mkstemp()
                    print fn
                    with open(fn, 'w') as f:
                        new_cmd = cmd.replace('%s',fn)
                        res = check_output(new_cmd, stderr=STDOUT, shell=True)
                    except CalledProcessError as e:
                        display.warning('Error occured while calling prediff_cmd "{}": (Code {}) {}'.format(cmd, e.returncode, e.output))
                        res = None
                    if res:
                        d[ab] = res
            return diff

        if 'prediff_cmd' in result._task_fields['vars']:
            prediff_cmd = result._task_fields['vars']['prediff_cmd']
            if result._task.loop and 'results' in result._result:
                for res in result._result['results']:
                    if 'diff' in res and res['diff'] and res.get('changed', False):
                        res['diff'] = process_diff(res['diff'], prediff_cmd)
            elif 'diff' in result._result and result._result['diff'] and result._result.get('changed', False):
                result._result['diff'] = process_diff(result._result['diff'], prediff_cmd)
        return super(CallbackModule, self).v2_on_file_diff(result)

Then you can set prediff_cmd as task's variable with %s being replaced with temporary file:

- copy:
    src: cert.pem
    dest: /tmp/cert.pem
    prediff_cmd: openssl x509 -in %s -noout -text | head -n 10

And set default callback, for example:

ANSIBLE_STDOUT_CALLBACK=diffhack ansible-playbook -vv --check --diff test.yml

To see this result:

TASK [copy] ***************************
--- before: /tmp/cert.pem
+++ after: /path/to/local/cert.pem
@@ -2,9 +2,9 @@
         Version: 3 (0x2)
         Serial Number:
-            aa:fc:53:d8:29:d2:8a:58
+            d2:05:f8:5c:61:ff:9e:d3
     Signature Algorithm: sha256WithRSAEncryption
         Issuer: CN=localhost
-            Not Before: Oct 13 07:32:01 2017 GMT
-            Not After : Oct 13 07:32:01 2018 GMT
+            Not Before: Oct 13 07:32:25 2017 GMT
+            Not After : Oct 13 07:32:25 2018 GMT
  • This is awesome, but what about modules, which have no diff-method at all now? Like command or shell, for example? I'll update my question with another example. – Mikhail T. Oct 16 '17 at 19:58
  • 1
    In this case I'd opted to write my custom module with desired behaviour. I see no easy to use extension to introduce diff. – Konstantin Suvorov Oct 17 '17 at 6:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.