Recently I had to dive back into Ansible playbooks I wrote (quite) some time ago. I had to add some logic to generate different application templates based on facts/packages being installed on the managed nodes. Long story short (I'll not describe the use case here as it's quite complex), I decided that injecting directly some kind of logic in the Jinja2 templates was enough .. but not.

Let's take a very simplified example here (don't even look at the tasks but rather at the logic explained how to get there, once again this is a 'stupid' playbook) :

---  
- hosts: localhost  
  connection: local  
  user: root  
  vars:  
    - myrole: httpserver

  tasks:  
    - name: registering a variable only if myrole is httpserver  
      command: /bin/rpm -q --qf '%{version}' httpd  
      register: httpd_version  
      when: myrole == 'httpserver'  
    - name: pushing the generated template  
      template: src=../templates/logic.txt.j2 dest=/tmp/logic.txt

 handlers:

Now let's have a look at the (very) simple logic.txt.j2 :

{% if httpd_version is defined -%}  
 You're using an Apache http server version : {{ httpd_version.stdout> }}  
{% else %}  
  You're not using an http server, or not defined in the ansible> machine role  
{% endif -%}

Easy, and it seems it was working when myrole was indeed httpserver :

 cat /tmp/logic.txt  
  You're using an Apache http server version : 2.2.15

But things didn't work as expected when myrole was something else, like for example dbserver

 TASK: [registering a variable only if myrole is httpserver]
 \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
  skipping: [localhost]

 TASK: [pushing the generated template]
 \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
  fatal: [localhost] =\> {'msg': "One or more undefined variables:
 'dict' object has no attribute 'stdout'", 'failed': True}

hmm, as the register: task was skipped, I was wondering why it then complained about the httpd_version.stdout as I thought that httpd_version wasn't defined .. but I was wrong  : even if 'skipped' the variable exists for that host. I quickly discovered it when adding a debug task in between the other tasks in my playbook :

- debug: msg="this is http_version value {{ httpd_version }}"

Now let's see what can be wrong :

 TASK: [debug msg="this is http_version value {{httpd_version}}"]
 \*\*\*\*\*\*\*\*\*\*\*\*\*\*  
  ok: [localhost] =\> {"msg": "this is http_version value {u'skipped': True, u'changed': False}"}

Very interesting : so even when skipped, the variable httpd_version is still "registered" by the register: feature but marked as skipped.

Let's so change our "logic" in the Jinja2 template then ! :

{% if httpd_version.skipped -%}  
  You're not using an http server, or not defined in the ansible machine role  
{% else %}  
  You're using an Apache http server version : {{ httpd_version.stdout }}  
{% endif -%}

And now it works in all cases ..

It's a (very,very,very) simplified example, but you get the idea and using the debug module (don't forget to call ansible-playbook with -vvv to see those messages too !) can quickly show you where your issue is when having to troubleshoot something. As Patrick Debois was saying : "you gotta love Ansible for its simplicity" :-)