Playbook action fails when template host fact host not available

I have a playbook for installing and configuring Collectd. This has a firewall script which has this jinja2 template for loop:

{% for host in groups[‘collectd-clients’] %}
{% if hostvars[host][‘ansible_default_ipv4’][‘address’] %}

Host {{ hostvars[host][‘inventory_hostname’] }}

{{ iptables_path }} -A INPUT -p udp -s {{ hostvars[host][‘ansible_default_ipv4’][‘address’] }} --dport {{ collectd_port }} -j ACCEPT
{% endif %}
{% if hostvars[host][‘ansible_default_ipv6’][‘address’] %}

Host {{ hostvars[host][‘inventory_hostname’] }}

{{ ip6tables_path }} -A INPUT -p udp -s {{ hostvars[host][‘ansible_default_ipv6’][‘address’] }} --dport {{ collectd_port }} -j ACCEPT
{% endif %}
{% endfor %}

However, when a host is not available but is in the client list (because for example downtime):

fatal: [vps24] => failed to transfer file to /setup:

ssh: connect to host vps24: Connection refused
Connection closed

The step which sets the template fails with the following error:

fatal: [vps21] => {‘msg’: “‘dict object’ has no attribute ‘ansible_default_ipv4’”, ‘failed’: True}

I understand that it does not have the variable available, but is it a bug or a feature that the templating step fails? And, how can I work around it, except ignore_failure?

This is the playbook template step, for reference:

  • template: src=config/iptables.sh dest=/tmp/iptables-collectd.sh owner=root group=root mode=0755
    only_if: “‘collectd-servers’ in $group_names”
    tags:
  • firewall

Yep, of course it does, if you need to use a variable that may not be present, you should set a default.

group_vars/all (or group_vars/groupname) is a great way to do that.