Pinning a quick guide to templating changes in Get Help

Hi everyone,

@gundalow and I have been talking about creating a sticky post for the “Get help” category that quickly outlines some of the changes coming with templating and points users to the porting guide and other resources.

When I say sticky post, I mean we’ll create this post and pin it to the top of “Get help”. We were thinking of posting this for the Ansible 12 release but maybe we could do that sooner.

We thought it would be a good idea to get some feedback and see if others think that’s a useful idea or not. So here we are.

Please expand the preview section below to read through the sticky post and let us know what you think.

Preview of the sticky post for templating changes

A quick guide to templating changes

Ansible 12 is built on the ansible-core stable-2.19 release, which includes an overhaul of the templating system. As the Porting Guide mentions, the overhaul of the ansible-core templating system now detects problematic behaviors that were not detected in previous versions.

Another way to think about that statement: In previous releases, you could have broken tests or playbooks that Ansible did not tell you about. As of version 12, Ansible will tell you about those broken tests or playbooks.

Of course when something is broken it needs to be fixed. And the purpose of this post is to help you find answers quickly and resolve issues with your playbooks, modules, collections, and roles.

Note that this document is not intended to be comprehensive. Think of this document as more of a quick start. You should always refer to the Porting Guide as the official source of truth and definitive guide. We do not plan to add more examples or additional detail into this post.

Detection of broken conditionals

Ansible 12 now detects conditional statements that were broken but previously ignored. This change can help identify logic errors that might have been causing silent issues in playbooks. The Porting Guide refers to these changes in the broken conditionals section.

The templating system now requires conditionals to evaluate to actual boolean values rather than relying on Python’s “truthy” evaluation. This means that ansible-core 2.19 and Ansible 12 detects non-boolean conditionals during expression evaluation and reports errors, for example:

# Before (silently broken)
- assert:
    that: inventory_hostname # Always truthy

In this example, inventory_hostname is an Ansible magic variable that contains the name of the current host from the inventory. For example it could be test-server1 which evaluates to a truthy value in a boolean context because it is not an empty string. This means the assertion is likely to always evaluate to True regardless of what the hostname might be.

With the change to evaluate conditionals to boolean values, you could update tests to be more meaningful as in the following examples:

# Test if the hostname is defined and not empty
- assert:
    that: inventory_hostname | length > 0

# Test if the hostname matches the expected value
- assert:
    that: inventory_hostname == "test-server1"

In this way, Ansible 12 now helps you catch conditionals that don’t actually test what you intended them to test and prevents tests from silently passing when they should fail.

Note that ansible-core will report errors with this message: Conditionals must have a boolean result. You can temporarily change the error to a deprecation warning with the ALLOW_BROKEN_CONDITIONALS configuration option.

See the Porting Guide for more information and complete details about broken conditionals.

Replacing embedded templates

One of the changes from the templating system overhaul is to get rid of the potentially dangerous ability to embed templates. The Porting Guide refers to this in the multi-pass templating section.

Let’s look at a real-world example. In a test for the community.mysql collection, the following change was required:

Before

- name: assert that databases does not exist
  assert:
    that:

    - "'{{ db1_name }}' not in mysql_result.stdout"
    - "'{{ db2_name }}' not in mysql_result.stdout"
    - "'{{ db3_name }}' not in mysql_result.stdout"

After

- name: assert that databases does not exist
  assert:
    that:

    - db1_name not in mysql_result.stdout
    - db2_name not in mysql_result.stdout
    - db3_name not in mysql_result.stdout

Explanation

The original code used template delimiters {{ }} within conditional expressions. This is an example of multi-pass templating because Ansible’s templating engine has to process those template delimiters multiple times.

In the first pass, Ansible “sees” this as a string that contains template syntax. Ansible recognizes that {{ db1_name }} needs to be substituted with an actual value; for example, testdb1.

When that template substitution happens, the string becomes "'testdb1' not in mysql_result.stdout".

In the second pass, Ansible evaluates that string as a conditional expression and checks if the literal string 'testdb1' is in mysql_result.stdout.

The updated code removes template delimiters so that it’s a single-pass operation where db1_name is directly referenced as a variable in the Jinja expression. As the Porting Guide mentions, “dynamic expression construction from playbooks is insecure and unsupported.”

Additional considerations

Ansible 12 includes a number of other changes beyond overhauling the templating engine. For example, module developers must now explicitly convert non-string keys to string before passing dictionaries to ansible-core’s AnsibleModule.exit_json() method. The Porting Guide refers to this in the No implicit conversion of non-string dict keys section.

Let’s see another real-world example. This time we can look at the community.clickhouse collection, which made the following change:

Before

if result == PRIV_ERR_CODE:

return {PRIV_ERR_CODE: "Not enough privileges"}

After

if result == PRIV_ERR_CODE:

return {str(PRIV_ERR_CODE): "Not enough privileges"}

Explanation

By wrapping PRIV_ERR_CODE with str() the module explicitly converts the integer, or numeric, constant to a string before using it as a dictionary key. This change makes data serialization more predictable and, since data handling is more explicit, there is also a security improvement.

Where to go next

Read the Porting Guide as your next step. The Porting Guide outlines all the changes in ansible-core’s templating engine as of stable-2.19 and Ansible 12. The Porting Guide also describes common errors that can occur due to those changes along with lots of examples.

Here are some other resources you might find useful to understand changes in Ansible 12:

Call to action

As always we’d love to hear from you. If you have questions or notice issues or small additions for this post, please reply directly below.

If you think you see important information that is missing from the Porting Guide, please create an issue here:

If you still have a question or are observing behavior or an error that the Porting Guide does not explain, please create a new forum post and add the “templating” tag.

4 Likes

@oranod, I like this a lot, and really appreciate the effort put into it! Thanks!

1 Like

@oranod should we go ahead and pin this?

1 Like

@samccann Do you know how to do that and would you mind? Seems fine to me.

The target audience of this post feels a little muddled. “Converting non-string keys to strings” isn’t a templating change, and is really something for module developers to be aware of rather than a thing most users will have to care about (maybe when they access the returned values, though that’s not covered in the proposed text.) On the other hand, the major change to how conditionals work is related to templating and will impact far more users but is not surfaced in the post.

1 Like

Thanks very much for the honest feedback @flowerysong That’s really helpful. I think I left out the info about the conditionals because the Porting Guide has a lot of detail on them and I didn’t want to reproduce too much of that or make that post a substitute for the Porting Guide. The goal is more to give an overview and point users to the right place for help, rather than trying to provide all the help.

Reading again, though, you make a solid point. It shouldn’t overlook the details about conditionals. I’ll update the preview draft there now.

Cheers!

I’ve created the pinned post here: A quick guide to templating changes

Feel free to let me know about any other nits or comments on the post here. Or reply directly to the pinned post. Thanks!

1 Like