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.
Examples of required changes
In addition to the issues and examples listed in the Porting Guide, here you can find some examples of changes you might need to make for Ansible 12 and ansible-core 2.19.
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.”
Converting non-string keys to strings
In Ansible 12, modules must explicitly convert any 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.
- Porting Guide at Ansible 12 Porting Guide — Ansible Community Documentation
Here are some other resources you might find useful to understand changes in Ansible 12:
- Core-2.19 templating changes - preview and testing
- Making a collection compatible with core 2.19 and templating changes
- Brian Coca’s description of templating changes: Core-2.19 templating changes - preview and testing - #20 by bcoca
- All forum posts about templating: Topics tagged templating
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.