Introduction
Jinja2 inline conditionals let you set variables dynamically in Ansible — choosing between values based on conditions, all within a single expression. The most common pattern is the ternary (if-else) expression, but there are several approaches depending on your use case.
The Ternary Pattern
Basic Syntax
# Jinja2 inline if-else
variable: "{{ 'value_a' if condition else 'value_b' }}"
Kubernetes Service Type Example
# Toggle between NodePort and ClusterIP
service_type: "{{ 'NodePort' if expose_service else 'ClusterIP' }}"
The Boolean Trap
The Problem
# BROKEN — compares boolean true to string 'true'
service_type: "{{ 'NodePort' if expose_service == 'true' else 'ClusterIP' }}"
When expose_service is a boolean true, comparing it to the string 'true' fails silently — the condition evaluates to false, and you always get ClusterIP.
The Fix: Use | bool
# CORRECT — works with bool true, string 'true', 'yes', '1'
service_type: "{{ 'NodePort' if expose_service | bool else 'ClusterIP' }}"
The | bool filter normalizes any input to a proper boolean:
| Input | | bool Result |
|-------|----------------|
| true | True |
| 'true' | True |
| 'yes' | True |
| '1' | True |
| 1 | True |
| false | False |
| 'false' | False |
| 'no' | False |
| '0' | False |
| 0 | False |
The Ternary Filter
Ansible provides a dedicated ternary filter as an alternative:
# Using the ternary filter
service_type: "{{ expose_service | bool | ternary('NodePort', 'ClusterIP') }}"
# With a third value for undefined/null
debug_mode: "{{ enable_debug | ternary('verbose', 'quiet', 'default') }}"
Ternary vs Inline If-Else
# These are equivalent:
a: "{{ 'yes' if condition else 'no' }}"
b: "{{ condition | ternary('yes', 'no') }}"
The ternary filter is cleaner when chaining with other filters.
Playbook workflow
Generate a complete playbook from this tutorial
Use Ansible Jinja2 Conditionals — Ternary as the starting context and generate an editable Ansible playbook in the playground.
One anonymous generation is available. Do not paste passwords, private keys, tokens, or customer secrets.
Common Patterns
Default Values
# Use default when variable might be undefined
port: "{{ custom_port | default(8080) }}"
# Default with boolean check
log_level: "{{ 'debug' if verbose | default(false) | bool else 'info' }}"
Nested Conditions
# Multi-level selection
env_name: "{{ 'production' if is_prod | bool else ('staging' if is_staging | bool else 'development') }}"
Conditional List Items
# Include items conditionally
packages: >-
{{ ['nginx', 'curl'] +
(['redis'] if use_cache | bool else []) +
(['postgresql-client'] if use_db | bool else []) }}
String Manipulation with Conditions
# Add suffix conditionally
hostname: "{{ base_name }}{{ '-dev' if env == 'development' else '' }}"
# Select from mapping
region_endpoint: "{{ {'us': 'us-east-1', 'eu': 'eu-west-1', 'ap': 'ap-southeast-1'}[region] | default('us-east-1') }}"
Practical Examples
Kubernetes Deployment
vars:
expose_service: true
replicas_count: "{{ 3 if env == 'production' else 1 }}"
resource_limits: "{{ '2Gi' if env == 'production' else '512Mi' }}"
image_tag: "{{ 'latest' if env == 'development' else version }}"
service_type: "{{ expose_service | bool | ternary('NodePort', 'ClusterIP') }}"
Platform-Specific Configuration
- name: Set package manager command
ansible.builtin.set_fact:
pkg_manager: "{{ 'dnf' if ansible_os_family == 'RedHat' else 'apt' }}"
service_name: "{{ 'httpd' if ansible_os_family == 'RedHat' else 'apache2' }}"
config_path: "{{ '/etc/httpd/conf/httpd.conf' if ansible_os_family == 'RedHat' else '/etc/apache2/apache2.conf' }}"
Feature Flags
vars:
features:
ssl: true
monitoring: false
cache: true
tasks:
- name: Configure features
ansible.builtin.template:
src: config.j2
dest: /etc/myapp/config.yml
vars:
ssl_enabled: "{{ features.ssl | bool | ternary('on', 'off') }}"
monitoring_endpoint: "{{ 'http://monitor:9090' if features.monitoring | bool else omit }}"
cache_backend: "{{ 'redis' if features.cache | bool else 'memory' }}"
Conditional when vs Ternary
# Use 'when' for skipping entire tasks
- name: Install Redis
ansible.builtin.dnf:
name: redis
state: present
when: use_cache | bool
# Use ternary for choosing between values within a task
- name: Configure cache backend
ansible.builtin.lineinfile:
path: /etc/myapp/config.yml
regexp: '^cache_backend'
line: "cache_backend: {{ use_cache | bool | ternary('redis', 'memory') }}"
Common Mistakes
Comparing Booleans to Strings
# WRONG
result: "{{ 'yes' if my_var == 'true' else 'no' }}"
# CORRECT
result: "{{ 'yes' if my_var | bool else 'no' }}"
Missing Quotes Around Jinja2 Expressions
# WRONG — YAML interprets { as a mapping
result: {{ 'a' if true else 'b' }}
# CORRECT — always quote Jinja2 expressions
result: "{{ 'a' if true else 'b' }}"
Undefined Variables
# WRONG — fails if my_var is undefined
result: "{{ 'yes' if my_var else 'no' }}"
# CORRECT — use default filter
result: "{{ 'yes' if my_var | default(false) | bool else 'no' }}"
Related Articles
- Ansible Best Practices Guide
- Ansible Debug Module Guide
- Avoid Variable Collisions with loop_control
- Ansible-Lint Guide
Conclusion
Use | bool whenever comparing variables that might be strings or booleans. Prefer the ternary filter for clean, chainable expressions, and | default() for undefined variables. The pattern {{ variable | default(false) | bool | ternary('value_a', 'value_b') }} handles every edge case — undefined, string, or boolean input.