Ansible ternary Filter: Inline If-Else Conditional in Jinja2 (Guide)
By Luca Berton · Published 2024-01-01 · Category: installation
How to use the Ansible ternary filter for inline if-else conditionals in Jinja2 templates. Simplify variable assignment, default values, boolean logic.
The ternary filter is Ansible's inline if-else — it returns one value when a condition is true, another when false. It replaces verbose when + set_fact patterns with a single expression.
Basic Syntax
{{ condition | ternary('value_if_true', 'value_if_false') }}
- name: Set package manager based on OS
ansible.builtin.set_fact:
pkg_manager: "{{ (ansible_facts['os_family'] == 'Debian') | ternary('apt', 'dnf') }}"
- name: Show result
ansible.builtin.debug:
msg: "Using {{ pkg_manager }}"
# Debian → "Using apt"
# RedHat → "Using dnf"
See also: Ansible combine Filter: Merge Dictionaries & Override Defaults (Guide)
Common Patterns
Boolean to String
# Convert true/false to human-readable
- ansible.builtin.debug:
msg: "SSL is {{ ssl_enabled | ternary('enabled', 'disabled') }}"
# Convert to yes/no for config files
- ansible.builtin.lineinfile:
path: /etc/myapp/config.conf
line: "use_ssl = {{ ssl_enabled | ternary('yes', 'no') }}"
Environment-Based Config
vars:
env: production
debug_mode: "{{ (env == 'development') | ternary(true, false) }}"
log_level: "{{ (env == 'production') | ternary('WARNING', 'DEBUG') }}"
replicas: "{{ (env == 'production') | ternary(3, 1) }}"
db_host: "{{ (env == 'production') | ternary('db-prod.example.com', 'localhost') }}"
tasks:
- name: Deploy with environment config
ansible.builtin.template:
src: app-config.j2
dest: /etc/myapp/config.yml
Conditional Package Names
- name: Install correct package for OS
ansible.builtin.package:
name: "{{ (ansible_facts['os_family'] == 'Debian') | ternary('apache2', 'httpd') }}"
state: present
Conditional Service Names
- name: Restart web server
ansible.builtin.service:
name: "{{ (ansible_facts['os_family'] == 'Debian') | ternary('apache2', 'httpd') }}"
state: restarted
Three-Value Ternary (with None/Undefined)
The ternary filter accepts a third argument for None values:
{{ value | ternary('true_val', 'false_val', 'none_val') }}
# Handle undefined/none variables
- ansible.builtin.debug:
msg: "{{ feature_flag | ternary('enabled', 'disabled', 'not configured') }}"
# true → "enabled"
# false → "disabled"
# None → "not configured"
See also: Ansible dict2items Filter: Convert Dictionaries to Lists (Guide)
Nested Ternary (Multiple Conditions)
# Equivalent to if/elif/else
- ansible.builtin.set_fact:
instance_size: >-
{{ (env == 'production') | ternary('m5.xlarge',
(env == 'staging') | ternary('t3.large',
't3.micro')) }}
Cleaner with Jinja2 if/elif:
# For 3+ conditions, Jinja2 if/elif is more readable
- ansible.builtin.set_fact:
instance_size: >-
{% if env == 'production' %}m5.xlarge
{% elif env == 'staging' %}t3.large
{% else %}t3.micro{% endif %}
ternary in Templates
{# nginx.conf.j2 #}
server {
listen {{ ssl_enabled | ternary('443 ssl', '80') }};
server_name {{ server_name }};
{% if ssl_enabled %}
ssl_certificate {{ cert_path }};
ssl_certificate_key {{ key_path }};
{% endif %}
access_log {{ (env == 'production') | ternary('/var/log/nginx/access.log combined', 'off') }};
location / {
proxy_pass http://{{ backend_host }}:{{ backend_port }};
proxy_set_header X-Forwarded-Proto {{ ssl_enabled | ternary('https', 'http') }};
}
}
See also: Ansible regex_search & regex_replace Filters: Pattern Matching Guide
ternary in Loops
- name: Create users with conditional shells
ansible.builtin.user:
name: "{{ item.name }}"
shell: "{{ item.admin | ternary('/bin/bash', '/bin/sh') }}"
groups: "{{ item.admin | ternary('wheel,docker', 'users') }}"
loop:
- { name: alice, admin: true }
- { name: bob, admin: false }
- { name: carol, admin: true }
ternary with Default Values
# Combine with default filter for undefined variables
- ansible.builtin.set_fact:
port: "{{ (custom_port is defined) | ternary(custom_port, 8080) }}"
# Shorter with just default:
port: "{{ custom_port | default(8080) }}"
# ternary is better when the logic isn't just "undefined":
port: "{{ (ssl_enabled | default(false)) | ternary(443, 80) }}"
ternary vs when vs Jinja2 if
| Pattern | Best For |
|---------|----------|
| ternary | Inline value selection in a single expression |
| when | Skip/run entire tasks conditionally |
| Jinja2 {% if %} | Multi-line template logic |
| default() | Provide fallback for undefined variables |
# ternary — one-liner value selection
db_port: "{{ (db_type == 'postgres') | ternary(5432, 3306) }}"
# when — skip entire task
- name: Install PostgreSQL
ansible.builtin.package:
name: postgresql
when: db_type == 'postgres'
# Jinja2 if — template blocks
{% if db_type == 'postgres' %}
[postgresql]
port = 5432
{% else %}
[mysql]
port = 3306
{% endif %}
FAQ
What is the Ansible ternary filter?
The ternary filter is a Jinja2 filter that returns one of two (or three) values based on a boolean condition. Syntax: {{ condition | ternary(true_value, false_value) }}. It's equivalent to the ternary operator (?:) in other programming languages.
Can I chain multiple ternary filters?
Yes, but nested ternary filters become unreadable fast. For 2 conditions, nesting is fine. For 3+, use Jinja2 {% if %}{% elif %}{% else %} blocks or a dictionary lookup:
# Dictionary lookup (cleaner than nested ternary)
sizes:
production: m5.xlarge
staging: t3.large
development: t3.micro
instance_size: "{{ sizes[env] | default('t3.micro') }}"
Does ternary work with undefined variables?
If the condition variable is undefined, ternary raises an error. Always use default() or is defined check:
# Safe pattern
{{ (my_var | default(false)) | ternary('yes', 'no') }}
What's the difference between ternary and the Jinja2 inline if?
They're equivalent. Jinja2 also supports inline if: {{ 'yes' if condition else 'no' }}. The ternary filter is more Ansible-idiomatic; inline if is standard Jinja2. Both work.
Conclusion
The ternary filter is the cleanest way to write inline conditionals in Ansible. Use it for variable assignments, template values, and anywhere you need a one-liner if-else. For complex multi-branch logic, switch to Jinja2 {% if %} blocks or dictionary lookups.
Related Articles
• Ansible Conditionals and when Guide • Ansible map vs selectattr vs json_query Guide • Ansible set_fact vs vars vs extra_vars Guide • Ansible Jinja2 Templates GuideCategory: installation