AnsiblePilot — Master Ansible Automation

AnsiblePilot is the leading resource for learning Ansible automation, DevOps, and infrastructure as code. Browse over 1,400 tutorials covering Ansible modules, playbooks, roles, collections, and real-world examples. Whether you are a beginner or an experienced engineer, our step-by-step guides help you automate Linux, Windows, cloud, containers, and network infrastructure.

Popular Topics

About Luca Berton

Luca Berton is an Ansible automation expert, author of 8 Ansible books published by Apress and Leanpub including "Ansible for VMware by Examples" and "Ansible for Kubernetes by Example", and creator of the Ansible Pilot YouTube channel. He shares practical automation knowledge through tutorials, books, and video courses to help IT professionals and DevOps engineers master infrastructure automation.

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 GuideAnsible map vs selectattr vs json_query GuideAnsible set_fact vs vars vs extra_vars GuideAnsible Jinja2 Templates Guide

Category: installation

Browse all Ansible tutorials · AnsiblePilot Home