Templating with Jinja2
Relevant source files
The following files were used as context for generating this wiki page:- lib/ansible/_internal/_templating/_engine.py
- lib/ansible/_internal/_templating/_jinja_bits.py
- lib/ansible/modules/template.py
- lib/ansible/plugins/lookup/template.py
- lib/ansible/plugins/doc_fragments/template_common.py
- test/units/_internal/templating/test_templar.py
- test/integration/targets/template/tasks/main.yml
- lib/ansible/plugins/filter/core.py
- lib/ansible/plugins/test/core.py
Ansible's templating system is a powerful feature that leverages the Jinja2 templating language to generate dynamic files and evaluate expressions. This allows for the creation of configuration files, scripts, and other text-based resources that are customized based on variables, host facts, and other data available during a playbook run. The core of this system is the TemplateEngine, which processes Jinja2 templates, and is exposed to users through various mechanisms like the template module, the template lookup plugin, and expression evaluation in playbooks.
The system is designed to be both flexible and secure, with a custom Jinja2 environment that adds Ansible-specific filters, tests, and lookups. It also includes a trust model to prevent the execution of untrusted template code. The engine handles variable resolution, error management, and the final rendering of data structures, making it a fundamental component of Ansible's execution flow.
Core Templating Engine
The heart of Ansible's templating is the TemplateEngine class, which manages the entire process of compiling and rendering Jinja2 templates.
Sources: lib/ansible/_internal/_templating/_engine.py
Architecture
The TemplateEngine orchestrates the interaction between variables, the Jinja2 environment, and the template data. It is responsible for initializing the environment, loading templates, providing variables, and finalizing the output.
The diagram below shows the main components of the templating engine.
This class diagram illustrates the relationship between TemplateEngine, its configuration via TemplateOptions, and the underlying AnsibleEnvironment.
Sources: lib/ansible/_internal/_templating/_engine.py:48-101
Templating Process
The primary method for rendering is template(). It takes input data, which can be a string, a list, or a dictionary, and recursively templates it.
The general flow for templating a string is as follows:
- Trust Check: The engine verifies if the string is marked as
TrustedAsTemplate. If not, it may raise aTemplateTrustCheckFailedErroror issue a warning, depending on configuration. - Compile: The string is compiled into a Jinja2
Templateobject using a customizedAnsibleEnvironment. This step also handles special#jinja2:headers for overriding delimiters. - Render: The compiled template is rendered with the available variables.
- Post-process: The rendered output is post-processed to, for example, preserve trailing newlines.
- Finalize: The result is finalized, which involves rendering any nested lazy objects and handling special markers like
Omit.
Sources: lib/ansible/_internal/_templating/_engine.py:202-276
This sequence diagram shows a simplified view of the template() method call.
Key TemplateEngine Methods
| Method | Description |
|---|---|
template() | The main entry point for templating data. It recursively processes strings, lists, and dictionaries. |
evaluate_expression() | Evaluates a single trusted Jinja2 expression string and returns its result. |
evaluate_conditional() | Evaluates a conditional expression and returns a boolean. It handles "broken conditionals" for backward compatibility. |
is_template() | Checks if the input data contains a Jinja2 template, even if the syntax is invalid. |
Sources: lib/ansible/_internal/_templating/_engine.py:202, 351, 448, 335
Jinja2 Environment and Extensions
Ansible uses a customized Jinja2 environment, AnsibleEnvironment, to provide additional functionality tailored for automation.
Sources: lib/ansible/_internal/_templating/_jinja_bits.py
AnsibleEnvironment
This class extends Jinja2's Environment to:
- Load Ansible's custom filters and tests.
- Handle Ansible-specific error classes like
AnsibleUndefinedVariable. - Support template overrides via a special
#jinja2:header in template files.
Sources: lib/ansible/_internal/_templating/_jinja_bits.py:112-116
Template Overrides
Jinja2 settings can be overridden on a per-template basis using a special header comment. This is useful for files that contain syntax that conflicts with Jinja2's default delimiters.
Example of an override header:
#jinja2: variable_start_string:'[%', variable_end_string:'%]', trim_blocks: False
This changes the variable delimiters to [% var %] instead of {{ var }} for that specific template.
Sources: lib/ansible/plugins/doc_fragments/template_common.py:80-84
Custom Filters and Tests
Ansible provides a rich set of custom filters and tests that can be used within templates. These are loaded into the Jinja2 environment and supplement the built-in ones.
- Filters: Functions that transform data. Examples include
to_json,to_yaml,b64encode, andcomment. - Tests: Functions that return a boolean, used for checks in
ifstatements. Examples includedefined,version, andsuccess.
Sources: lib/ansible/plugins/filter/core.py, lib/ansible/plugins/test/core.py
Usage in Ansible
Templating is exposed to users in several ways.
ansible.builtin.template Module
This is the primary module for rendering a template file from the Ansible controller to a destination on a remote host.
Sources: lib/ansible/modules/template.py, lib/ansible/plugins/doc_fragments/template_common.py
Key Parameters:
| Parameter | Description | Default |
|---|---|---|
src | Path to the Jinja2 template file on the controller. | (required) |
dest | Path to render the template to on the remote machine. | (required) |
mode, owner, group | Standard file permission attributes. | - |
newline_sequence | Specifies the newline sequence (\n, \r, \r\n). | \n |
variable_start_string | The string marking the beginning of a print statement. | {{ |
variable_end_string | The string marking the end of a print statement. | }} |
block_start_string | The string marking the beginning of a block. | {% |
block_end_string | The string marking the end of a block. | %} |
trim_blocks | If yes, the first newline after a block is removed. | yes |
lstrip_blocks | If yes, strips leading spaces and tabs from the start of a line to a block. | no |
validate | A command to run to validate the templated file before moving it into place. | - |
Sources: lib/ansible/plugins/doc_fragments/template_common.py:16-77
Example Task:
- name: Template a file to /etc/file.conf
ansible.builtin.template:
src: /mytemplates/foo.j2
dest: /etc/file.conf
owner: bin
group: wheel
mode: '0644'
Sources: lib/ansible/modules/template.py:59-65
ansible.builtin.template Lookup
The template lookup plugin renders a template file and returns its content as a string, which can then be used as a variable in a playbook.
Sources: lib/ansible/plugins/lookup/template.py
Key Options:
| Option | Description | Default |
|---|---|---|
_terms | A list of template files to process. | (required) |
template_vars | A dictionary of additional variables for templating. | {} |
variable_start_string | Custom start string for variables. | {{ |
variable_end_string | Custom end string for variables. | }} |
trim_blocks | Determines if newlines should be removed from blocks. | yes |
Sources: lib/ansible/plugins/lookup/template.py:18-73
Example Usage:
- name: show templating results
ansible.builtin.debug:
msg: "{{ lookup('ansible.builtin.template', './some_template.j2') }}"
Sources: lib/ansible/plugins/lookup/template.py:79-81
Special Template Variables
When a template is rendered, Ansible makes several special variables available within the template's context.
| Variable | Description |
|---|---|
ansible_managed | A configurable string indicating the file is managed by Ansible, including timestamp and owner info. |
template_host | The node name of the template's machine. |
template_path | The path of the template file. |
template_fullpath | The absolute path of the template file. |
template_destpath | The destination path of the template on the remote system. |
template_run_date | The date the template was rendered. |
Sources: lib/ansible/plugins/doc_fragments/template_common.py:10-15
Security and Trust Model
To prevent arbitrary code execution from untrusted data sources, Ansible's templating engine includes a trust model.
TrustedAsTemplate
Strings that are intended to be templated must be wrapped in a special class or marked with a tag. The TemplateEngine checks for this trust marker before attempting to compile and render a string. If an untrusted string containing Jinja2 syntax is passed to the engine, it will typically be treated as a literal string, and a warning or error will be generated.
The TrustedAsTemplate tag is automatically applied to values read from YAML files in roles and playbooks, but not to data from dynamic sources like inventory or module return values.
Sources: lib/ansible/_internal/_templating/_engine.py:452-463, test/units/_internal/templating/test_templar.py:70-84
Sandbox Mode
The Jinja2 environment can be run in a sandboxed mode to restrict access to unsafe attributes or methods within templates. The _TemplateConfig.sandbox_mode setting controls this behavior. By default, access to attributes starting with an underscore is blocked to prevent access to private methods.
Sources: test/units/_internal/templating/test_templar.py:1003-1013
Error Handling
The templating engine is designed to provide clear errors.
AnsibleTemplateSyntaxError: Raised for invalid Jinja2 syntax.AnsibleUndefinedVariable: Raised when a template references a variable that is not defined. The error message clearly indicates which variable is missing.AnsibleTemplatePluginNotFoundError: Raised when a specified filter, test, or lookup plugin cannot be found.- Recursive Loop Detection: The engine detects and errors out on recursive template references, such as
var: "{{ var }}", to prevent infinite loops.
Sources: lib/ansible/_internal/_templating/_engine.py:12-18, test/integration/targets/templating/tasks/main.yml:102-109
Conclusion
Ansible's integration with Jinja2 provides a deeply embedded and powerful system for generating dynamic content. Through the TemplateEngine, custom AnsibleEnvironment, and user-facing tools like the template module and lookup, developers have fine-grained control over file generation and data manipulation. The system's architecture prioritizes security through a data trust model while providing extensive flexibility with custom filters, tests, and per-template configuration overrides. Understanding this system is key to leveraging Ansible for complex configuration management tasks.