Understanding Ansible Templates: A Comprehensive Guide
Templates solve a fundamental challenge in configuration management: how to maintain consistency while accommodating necessary variations across systems.
Ansible templates are configuration files with dynamic elements that get rendered at runtime.
Unlike static files that remain identical when copied to target systems, templates can incorporate variables, conditionals, loops, and other programming constructs to generate customized files for each managed host.
For example, a web server configuration might need the same overall structure across all servers, but with different IP addresses, port settings, or resource allocations depending on the environment (development, testing, or production) and the specific role of each server.
In this comprehensive guide, we'll explore how Ansible templates work, see practical examples, and learn best practices for effective implementation.
Understanding Jinja2 template engine basics
Ansible templates use Jinja2, a modern and designer-friendly templating language for Python.
It provides the syntax and processing capabilities that make templates dynamic. Let's examine the fundamental elements of Jinja2 that you'll use in your Ansible templates.
Variables and expressions
Variables in Jinja2 are enclosed in double curly braces: {{ variable_name }}.
When Ansible processes a template, it replaces these variables with their actual
values from your inventory, playbooks, or role definitions.
For example, a simple template for an Apache virtual host might look like this:
When this template is processed, Ansible replaces the variables with their
actual values. If http_port is set to 80, admin_email to
admin@example.com, server_name to www.example.com, and doc_root to
"/var/www/html", the resulting file would be:
Control structures
Jinja2 offers control structures that enable you to add logic to your templates.
These structures include conditionals and loops, which are enclosed in {% %}
delimiters.
Conditionals
Conditionals let you include or exclude content based on certain conditions:
In this example, the SSL configuration is only included if the ssl_enabled
variable is set to true.
Loops
Loops allow you to iterate over collections like lists and dictionaries, generating repetitive content dynamically:
This template would generate a list of backend servers in HAProxy configuration
based on the web_servers list variable.
Filters
Filters transform data before it's rendered in the template. They are applied
using the pipe symbol (|):
In this example, the upper filter converts the hostname to uppercase. Ansible
has many built-in filters for string manipulation, data transformation, and more
complex operations.
Creating your first Ansible template
Now that you understand the basics, let's create a complete example of using templates in an Ansible playbook. We'll create a template for an Nginx virtual host configuration.
Step 1: Create the template file
First, create a templates directory in your Ansible project and add a new file
named nginx_vhost.conf.j2:
Step 2: Define variables
Next, define the variables either in your inventory, groupvars, hostvars, or directly in the playbook. Here's an example using variables in a playbook:
Step 3: Run the playbook
Execute the playbook to deploy the template:
The template module will process the template file, replace all variables and expressions with their values, and write the resulting configuration to the destination path on the target servers.
Variables in templates
Understanding variable sources and precedence is crucial when working with Ansible templates. Variables can come from multiple places, and knowing which value takes precedence can save you hours of debugging.
Variable sources
Variables in Ansible can come from:
- Inventory files (both static and dynamic).
- Group variables in
group_vars/directory. - Host variables in
host_vars/directory. - Variables defined in playbooks.
- Variables defined in roles.
- Extra variables passed at the command line with
-eor--extra-vars. - Facts gathered from remote systems.
Let's see a practical example of organizing variables for a multi-environment setup:
When these variables are used in a template, Ansible combines them according to
its variable precedence rules. Variables defined in hostvars override those in
groupvars, and variables passed at runtime with -e override almost everything
else.
Variable precedence
Understanding Ansible's variable precedence can help you design more predictable templates. In simplified order (from lowest to highest precedence):
- Role defaults.
- Inventory variables.
- Group variables.
- Host variables.
- Playbook variables (vars).
- Playbook vars_prompt.
- Playbook vars_files.
- Role and include variables.
- Block variables.
- Task variables.
- Extra variables (always win precedence).
Best practices for variable organization
For effective template management:
- Group related variables logically.
- Use descriptive variable names.
- Separate environment-specific variables.
- Document variable purpose and acceptable values.
For example, organize your variables by role and environment:
Advanced template techniques
Once you're comfortable with basic templates, you can leverage more advanced features to create sophisticated, adaptable configurations.
Complex conditionals
Jinja2 allows complex conditional logic within templates:
Here, we're using nested conditionals to create different Apache configurations based on both the environment and whether SSL is enabled.
Loops and iterations
Beyond basic for loops, Jinja2 provides several special variables within loops:
This example uses loop.first to mark the primary DNS server and loop.last to
properly space the search domains. Other useful loop variables include
loop.index, loop.revindex, and loop.length.
For more complex iterations, we can use nested loops:
This template creates multiple backend sections in an HAProxy configuration, each with its own set of servers.
Custom filters and transformations
Ansible extends Jinja2 with many custom filters. Let's see some useful ones:
Here we're using several filters:
to_uuidto generate a UUID from a stringboolto ensure a boolean valuejointo create a comma-separated listpassword_hashto securely hash a passwordintto convert to an integerto_jsonto format a complex variable as JSON
Another powerful transformation is the default filter, which provides a
fallback value when a variable is undefined:
Template deployment strategies
Properly deploying templates requires understanding the various options available in the template module.
The Ansible template module has many useful parameters:
This example demonstrates:
- Setting ownership and permissions.
- Creating backups before overwriting existing files.
- Validating the template output before deployment.
- Controlling whitespace in the rendered template.
Setting proper permissions is also crucial for security. The mode parameter
accepts both symbolic and numeric notation:
The validate parameter also runs a command to check the rendered file before
replacing the destination. This prevents deployment of invalid configurations:
The %s in the validate command is replaced with the path to a temporary file
containing the rendered template. If validation fails, the template isn't
deployed and the task fails.
Template security considerations
Security is a crucial aspect of configuration management. Here are some security considerations for Ansible templates.
Managing sensitive data
Avoid hardcoding sensitive data directly in templates. Instead:
- Use Ansible Vault for encrypting sensitive variables.
- Use environment-specific variable files that are properly secured.
- Consider using a secrets management system integrated with Ansible.
Here's an example of using encrypted variables in a template:
The corresponding playbook might look like:
To create encrypted variable files:
Using Ansible Vault with templates
Ansible Vault integrates seamlessly with templates. You can encrypt entire variable files or individual variables:
To use encrypted variables in playbooks and templates:
Or use a vault password file:
Final thoughts
Ansible templates transform infrastructure management through dynamic configuration generation. By organizing variables logically, implementing proper security, and leveraging roles and collections, you can build maintainable, scalable automation workflows.
Thanks for reading!