Ansible 12 Upgrade Guide: Breaking Changes, Data Tagging & What to Test First
By Luca Berton · Published 2024-01-01 · Category: installation
Complete Ansible 12 upgrade guide covering breaking changes in ansible-core 2.19, the new Data Tagging system, broken conditionals enforcement, multi-pass.
Ansible 12 ships with ansible-core 2.19, bringing the biggest internal change in years: a complete templating system overhaul and the new Data Tagging feature. These changes improve security and performance but introduce breaking changes that will affect many existing playbooks.
This guide walks through every breaking change, shows you what breaks and how to fix it, and gives you a pre-upgrade testing checklist.
What's New in Ansible 12 / ansible-core 2.19
| Feature | Impact | Action Required |
|---------|--------|----------------|
| Data Tagging | New metadata tracking for template data | Test playbooks — may expose hidden bugs |
| Broken Conditionals | Non-boolean conditionals now error | Fix when/assert statements |
| Multi-pass Templating Removed | Nested {{ }} in expressions blocked | Remove template delimiters from expressions |
| Stricter Jinja2 Evaluation | Order-of-operations bugs now caught | Add parentheses where needed |
| ALLOW_BROKEN_CONDITIONALS | Temporary escape hatch | Use for transition, don't rely on it |
See also: Ansible 13 Upgrade Guide: Breaking Changes, Removals, and Migration Steps
Pre-Upgrade Checklist
Before upgrading production systems:
# 1. Check current version
ansible --version
ansible-core --version
# 2. Create a virtualenv for testing
python3 -m venv ~/ansible12-test
source ~/ansible12-test/bin/activate
pip install ansible==12.0.0
# 3. Run playbooks in check mode first
ansible-playbook site.yml --check -v
# 4. Enable broken conditionals warning (don't error)
export ANSIBLE_ALLOW_BROKEN_CONDITIONALS=true
ansible-playbook site.yml --check -v 2>&1 | grep -i "broken conditional\|warning"
Breaking Change 1: Broken Conditionals
This is the change that will affect the most playbooks. ansible-core 2.19 now requires conditionals to return actual booleans, not truthy values.
What Breaks
# ❌ BREAKS in Ansible 12 — truthy string, not boolean
- name: Check hostname
ansible.builtin.assert:
that: inventory_hostname
# Error: Conditional result was 'myhost' of type 'str',
# which evaluates to True. Conditionals must have a boolean result.
How to Fix
# ✅ WORKS — explicit boolean result
- name: Check hostname is set
ansible.builtin.assert:
that: inventory_hostname | length > 0
# ✅ Also works — use 'is defined' or 'is truthy'
- name: Check hostname is defined
ansible.builtin.assert:
that: inventory_hostname is defined
Common Patterns That Break
# ❌ Truthy check on registered variable
- name: Run command
ansible.builtin.command: whoami
register: result
- name: Check result
ansible.builtin.debug:
msg: "Success"
when: result.stdout
# Fix → when: result.stdout | length > 0
# ❌ Accidental string in assert
- name: Bad assert
ansible.builtin.assert:
that: inventory_hostname is defined and 'inventory_hostname | length > 0'
# Fix → remove quotes: inventory_hostname is defined and inventory_hostname | length > 0
# ❌ Dictionary as conditional (YAML parsing trap)
- name: Check message
ansible.builtin.assert:
that:
- result.msg == "some_key: some_value"
# Fix → quote the whole expression:
# - 'result.msg == "some_key: some_value"'
# ❌ Jinja operator precedence
- name: Contains check
ansible.builtin.assert:
that: inventory_hostname is contains "local" ~ "host"
# Fix → add parentheses:
# that: inventory_hostname is contains("local" ~ "host")
Temporary Escape Hatch
If you need time to fix all your playbooks:
# ansible.cfg
[defaults]
allow_broken_conditionals = true
Or as environment variable:
export ANSIBLE_ALLOW_BROKEN_CONDITIONALS=true
Warning: This reduces errors to warnings. It's a migration aid, not a permanent solution. Fix your conditionals.
See also: ansible-core 2.19 Templating Changes: Fix Broken Conditionals & Jinja Errors
Breaking Change 2: Multi-Pass Templating Removed
ansible-core 2.19 no longer allows nested templates or template delimiters inside expressions. This prevents untrusted templates from being executed — a security improvement.
What Breaks
# ❌ BREAKS — template delimiters inside expression
- name: Calculate value
ansible.builtin.assert:
that: 1 + {{ value }} == 2
vars:
value: 1
# Error: Syntax error in expression. Template delimiters are not
# supported in expressions: expected token ':', got '}'
How to Fix
# ✅ WORKS — reference variable directly
- name: Calculate value
ansible.builtin.assert:
that: 1 + value == 2
vars:
value: 1
More Examples
# ❌ BREAKS — dynamic template construction
- name: Dynamic conditional
ansible.builtin.debug:
msg: "Dynamic"
when: "{{ my_condition }}"
# Fix → when: my_condition
# ❌ BREAKS — nested template in variable
- name: Nested template
ansible.builtin.debug:
msg: "Value is {{ '{{ inner_var }}' }}"
# Fix → msg: "Value is {{ inner_var }}"
# ❌ BREAKS — template in loop expression
- name: Loop with template
ansible.builtin.debug:
msg: "{{ item }}"
loop: "{{ {{ my_list }} }}"
# Fix → loop: "{{ my_list }}"
Breaking Change 3: Stricter Expression Syntax
Syntax errors that were previously silently ignored now produce errors.
# ❌ BREAKS — trailing comma (syntax error)
- name: Compare values
ansible.builtin.assert:
that: 1 == 2,
# Error: Syntax error in expression: chunk after expression
# Fix → remove the trailing comma: that: 1 == 2
See also: AAP 2.6 Migration from AWX: Complete Upgrade and Data Migration Guide
Data Tagging: What It Is
Data Tagging is the new internal mechanism that tracks metadata about template data as it flows through Ansible. It's what enables all the stricter checking above.
What it means for you: • Variables now carry metadata about their origin and type • Ansible can detect when untrusted data enters templates • Conditional evaluation is type-checked at runtime • Previously silent bugs are now surfaced
You don't need to change anything for Data Tagging itself — it's the enforcement mechanisms (broken conditionals, multi-pass removal) that require playbook changes.
Step-by-Step Upgrade Process
Step 1: Audit Your Playbooks
# Find potential broken conditionals
grep -rn "when:" roles/ playbooks/ | grep -v "is defined\|is not defined\|==\|!=\|>.*<\|in \|not in\|is true\|is false\|bool"
# Find nested templates
grep -rn "{{ {{ \|{{ '{{" roles/ playbooks/
# Find template delimiters in when/assert
grep -rn "when: \"{{ \|that: .* {{ " roles/ playbooks/
Step 2: Test in Staging
# test-upgrade.yml — Run against staging
- name: Test Ansible 12 compatibility
hosts: staging
gather_facts: true
tasks:
- name: Verify version
ansible.builtin.debug:
msg: "Running ansible-core {{ ansible_version.full }}"
- name: Test boolean conditional
ansible.builtin.debug:
msg: "Boolean conditionals work"
when: ansible_os_family == "Debian"
- name: Test explicit length check
ansible.builtin.assert:
that:
- inventory_hostname | length > 0
- ansible_distribution is defined
Step 3: Fix and Validate
# Run with verbose output to catch warnings
ansible-playbook site.yml --check -vv 2>&1 | tee upgrade-check.log
# Count issues
grep -c "broken conditional\|Syntax error\|Template delimiters" upgrade-check.log
Step 4: Upgrade Production
# Upgrade
pip install --upgrade ansible==12.0.0
# Verify
ansible --version
# ansible [core 2.19.x]
# Run smoke test
ansible-playbook smoke-test.yml
Version Compatibility Matrix
| Ansible Package | ansible-core | Python | Status | |----------------|-------------|--------|--------| | Ansible 12 | 2.19.x | 3.11+ | Current | | Ansible 11 | 2.18.x | 3.10+ | Maintained | | Ansible 10 | 2.17.x | 3.10+ | Security fixes | | Ansible 9 | 2.16.x | 3.10+ | EOL |
FAQ
What is Data Tagging in ansible-core 2.19?
Data Tagging is a new internal feature that tracks metadata about template data as it flows through Ansible. It enables stricter type checking on conditionals, prevents untrusted template execution, and surfaces bugs that previous versions silently ignored. You don't interact with Data Tagging directly — it powers the improved enforcement behind the scenes.
Will my existing playbooks break when upgrading to Ansible 12?
Possibly. The most common breakage is conditionals that rely on truthy evaluation instead of explicit boolean results. Use ALLOW_BROKEN_CONDITIONALS=true as a temporary migration aid, audit your playbooks with grep, and test in staging before upgrading production.
What is ALLOW_BROKEN_CONDITIONALS?
A configuration option in ansible-core 2.19 that temporarily reduces broken conditional errors to warnings. Set it in ansible.cfg or as an environment variable to buy time for migration. It's a transition tool — don't leave it enabled permanently.
How do I find all broken conditionals in my playbooks?
Search for when: and that: clauses that don't use explicit boolean operators (==, !=, is defined, is true/false, | bool, | length > 0). Run your playbooks with --check -vv on ansible-core 2.19 to get specific error messages for each broken conditional.
Can I run Ansible 11 and 12 side by side?
Yes — use Python virtualenvs. Create separate environments for each version and switch between them for testing. This is the recommended approach for migration.
Conclusion
Ansible 12 with ansible-core 2.19 is a significant upgrade that improves security and catches real bugs in your playbooks. The breaking changes — strict boolean conditionals and multi-pass templating removal — require attention, but every fix makes your automation more reliable. Test in staging, fix your conditionals, and upgrade with confidence.
Related Articles
• ansible-core 2.19 Templating Changes • Ansible Conditionals: when, assert & Conditional Execution • Ansible Jinja2 Filters Complete Reference • Ansible Check Mode / Dry Run GuideCategory: installation