Ansible Jinja2 Templates Advanced Patterns Prompt
Use advanced Jinja2 in Ansible — filters (default, map, selectattr), lookups, complex conditionals, custom filters.
- Target user
- Ansible engineers writing complex templates
- Difficulty
- Intermediate
- Tools
- Claude, ChatGPT
The prompt
You are a senior Ansible engineer who has written complex Jinja2 templates — config files, derived values, multi-line logic.
I will provide:
- The template need
- Current attempt
- Symptom (undefined errors, wrong value, syntax)
Your job:
1. **Common filters**:
- `default(value)` — fallback
- `mandatory` — fail if undefined
- `length`, `lower`, `upper`, `trim`
- `to_yaml`, `to_json`
- `b64encode`, `b64decode`
- `dict2items`, `items2dict`
2. **For list manipulation**:
- `select`, `reject`
- `selectattr`, `rejectattr`
- `map`
- `flatten`
3. **For dict manipulation**:
- `combine` (recursive merge)
- `dict2items` / `items2dict`
4. **For string**:
- `regex_search`, `regex_replace`
- `split`, `join`
- `format`
5. **For conditionals (Jinja)**:
- `{% if %}` / `{% else %}` / `{% endif %}`
- Ternary: `{{ x if cond else y }}`
6. **For loops in templates**:
- `{% for item in list %}`
- `loop.index`, `loop.first`, `loop.last`
7. **For lookups**:
- `lookup('env', 'VAR')`
- `lookup('file', 'path')`
- `lookup('template', 'path')`
- `lookup('vars', 'name')`
8. **For custom filters**:
- Python in `filter_plugins/` directory
- Register in playbook
Mark DESTRUCTIVE: complex inline logic instead of set_fact, undefined variable access without default (fails template), templates with side effects.
---
Template need: [DESCRIBE]
Current attempt: [PASTE]
Symptom: [DESCRIBE]
Why this prompt works
Templates can be complex. This prompt walks patterns.
How to use it
- Use filters for common ops.
- Default for safety.
- set_fact for complex logic.
- Test in debug first.
Examples
Defaults and safety
# Provide default
{{ web_port | default(80) }}
# Mandatory (fail if undefined)
{{ web_workers | mandatory }}
# Default + transform
{{ (web_log_level | default('info')) | upper }}
Complex list operations
# Filter list of dicts
{% for user in users | selectattr('enabled') | selectattr('shell', 'eq', '/bin/bash') %}
{{ user.name }}:{{ user.uid }}
{% endfor %}
# Map and join
{{ users | map(attribute='name') | join(',') }}
# Sort
{% for host in groups['web'] | sort %}
server {{ hostvars[host].ansible_host }};
{% endfor %}
Dict combine (recursive merge)
- name: Combine defaults with overrides
set_fact:
final_config: "{{ default_config | combine(env_config, recursive=True) }}"
String manipulation
# Regex
{{ version | regex_search('^v(\d+)') }}
# Split / join
{{ "hello,world" | split(',') | join(' ') }}
# Format string
{{ "%s:%d" % (host, port) }}
Conditionals
# If/else
{% if web_ssl_enabled %}
listen {{ web_port }} ssl;
ssl_certificate {{ web_ssl_cert }};
{% else %}
listen {{ web_port }};
{% endif %}
# Ternary
worker_processes {{ ansible_processor_vcpus if ansible_processor_vcpus else 4 }};
Loops in templates
{% for upstream in upstreams %}
upstream {{ upstream.name }} {
{% for server in upstream.servers %}
server {{ server.host }}:{{ server.port }};
{% endfor %}
}
{% endfor %}
# With index
{% for item in items %}
{{ loop.index }}: {{ item }}{% if not loop.last %},{% endif %}
{% endfor %}
Lookups
# From env
api_token: {{ lookup('env', 'API_TOKEN') }}
# From file
ssh_public_key: {{ lookup('file', '~/.ssh/id_rsa.pub') }}
# From dict
{{ lookup('vars', 'my_dynamic_var_name') }}
Custom filter
# filter_plugins/my_filters.py
def reverse_string(value):
return value[::-1]
def truncate(value, length=10):
return value[:length] + ('...' if len(value) > length else '')
class FilterModule:
def filters(self):
return {
'reverse_string': reverse_string,
'truncate': truncate,
}
{{ 'hello' | reverse_string }} # olleh
{{ long_text | truncate(20) }}
Multi-line for nginx-like
{% for app in apps %}
location /{{ app.name }} {
proxy_pass http://{{ app.backend }};
proxy_set_header Host $host;
{% if app.cache_enabled | default(false) %}
proxy_cache app_cache;
proxy_cache_valid 200 5m;
{% endif %}
}
{% endfor %}
Common findings this catches
undefinederrors → add default(…).- Complex inline logic → refactor to set_fact.
- Filters returning unexpected → test in debug.
- regex_search no match → check None handling.
- List filter on dict → use selectattr.
- Custom filter not found → directory or collection.
- YAML output odd → to_nice_yaml vs to_yaml.
When to escalate
- Complex template patterns — refactor.
- Custom filter library — collection.
- Performance issues — profile.
Related prompts
-
Ansible Loops & Control Prompt
Use Ansible loops correctly — loop vs with_items, complex loops, retries, loop_var, nested loops, performance.
-
Ansible Roles Structure Best Practices Prompt
Design Ansible roles — defaults vs vars, meta dependencies, role parameters, tags, idempotency.
-
Ansible Variable Precedence Prompt
Debug Ansible variable scope — precedence rules, override behavior, hostvars, magic vars, set_fact lifetime.