Are you over deploying manually config files that change from one environment to another? Done with managing seemingly-random parameters via those old-school, cryptic, regular expressions? Well then, welcome to a no-worries future where the Ansible template module is always here for you, no matter what sort of complicated logic you might need to throw at it. Read on for a 3-minute tutorial below, and if you’re interested in some background, you can also learn upfront why the Level Up team believes it’s one of the first ten Ansible modules you should know.

Background

Picture this: a simple yet precise tool that allows you to create and manage dynamic files with ease, taking care of all the atomic details of your various config files and more– while you sit back (and likely marvel at) the instantaneous powers of automation which going with Ansible has given you. A key foundational module, which leverages the widely-used Jinja2 templating engine, the template module is in many ways a central component of Ansible’s automation framework, because it allows you to accomplish so much customization, in such tiny expressions of code. With this builtin functionality, you can effortlessly generate and then actually maintain config files tailored to your specific environments and use cases– making those shimmering, off-in-the-distance visions of automation that you’ve probably had a reality, one which is now no farther away than your keyboard.

To understand how Jinja2 helps you go faster (and more safely) than the regex tactics of earlier times… Jinja2 templating offers dynamic (meaning at runtime), highly readable content generation using variables and control structures; whereas, using regex in things like bash scripts does allow complex text pattern matching and manipulation, but is seriously lacking in readability. What this also means is that for a given variable of text named {{ myvar }}, Jinja2 injects it PRECISELY wherever you place that {{ myvar }} in the actual file you want to copy over to the remote host’s filesystem (and if you put that variable in two places in the file, it injects it twice); meanwhile for newcomers especially, the inherent “match and replace” functionality of regex has a tendency to replace things in unexpected ways which they didn’t actually want it to, while also being capable of replacing text only once when they actually wanted it replaced multiple times, or every time it occurs in the file. There have been entire books written on regex. We’re pretty confident in saying that nobody has ever needed a Jinja2 book to figure out how to start templating their config files right away!

Also, here are some essential comparison points between related Ansible builtin modules:

  • Ansible Template Module:
    • Generates dynamic configuration files based on Jinja2 templates
    • Great for deploying files that change between environments
    • Requires a separate template file with variables
    • Performs full file updates
  • Ansible Copy Module:
    • Copies local files to remote hosts
    • Ideal for static files that don’t require customization
    • No templating or variable substitution functionality
    • Preserves file permissions and attributes
  • Ansible Lineinfile Module:
    • Modifies single lines in existing files
    • Suitable for small updates or adding individual configuration parameters
    • Regex-based search and replace functionality
    • Can add, modify, or remove lines in files
  • Ansible Blockinfile Module:
    • Inserts or updates multi-line blocks within existing files
    • Useful for managing sections of configuration files
    • Preserves the file structure while making changes
    • Limited to managing specific blocks within files

The Ansible template module is so easy to start using, we’re going to cover everything you need to know to get started in just 3 minutes:

Minute 1: The Absolute Basics

Ansible template module is almost always the right choice for managing your dynamic configuration files, primarily because it harnesses the power of Jinja2 templating:

  1. Assuming you already have Ansible installed and host or two to test this out on…
  1. Create a playbook:
# deploy-config-file.yml: 
---
- name: Deploy Dynamic Configuration File
  hosts: all
  vars:
    services:
      - name: httpd
        conf_path: /etc/httpd/httpd.conf
      - name: mysqld
        conf_path: /etc/mysql/my.cnf
  tasks:
    - name: Get SELinux mode 
      ansible.builtin.command:
        cmd: getenforce
      register: sel

    - name: Print SELinux mode
      ansible.builtin.debug:
        msg: "{{ sel.stdout }}"

    - name: Deploy template
      ansible.builtin.template:
        src: templates/imaginary.conf.j2
        dest: /tmp/imaginary.conf
  1. Design your template: We’re creating an imaginary config file to keep the line count low. Let’s write a Jinja2 template file for our imaginary config file named “templates/imaginary.conf.j2” in your source control repo. Now add your desired configurations and use some variables for dynamic content:
# Server:
listen {{ http_port }}
server_name {{ server_name }}
  1. Set variables: Define host variables for each host in your inventory file, simply called inventory (and note how Ansible can easily pull together variables from whatever sources you may give it, in a cohesive and seamless way):
# inventory:
[web]
host1 http_port=80 server_name=host1.example.com
host2 http_port=80 server_name=host2.example.com

Minute 2: Working with Loops and Conditionals

  1. Loops: Add this code block to “templates/imaginary.conf.j2” in order to iterate through a list of items in your template (and note how easy it is to inject these variables EXACTLY where you want them using Jinja2, without even having to think about classic regex challenges like whitespace):
# Services:
{% for service in services %}
        - {{ service.name }}: {{ service.conf_path }}
{% endfor %}
  1. Conditionals: Next add this to “templates/imaginary.conf.j2” to apply if-elif-else conditions to render content only when specific criteria are met:
# SELinux:
{% if sel.stdout == "Enforcing" %}
"SELinux is enforcing on this system."
{% elif sel.stdout == "Permissive" %}
"SELinux is permissive on this system."
{% elif sel.stdout == "Disabled" %}
 "SELinux is disabled on this system."
{% else %}
"SELinux is probably not installed on this system."
{% endif %}

Minute 3: Executing the Playbook

  1. Run your playbook: Execute the following command, and watch your new config file appear like magic on the file system!
ansible-playbook -i inventory deploy-config-file.yml
  1. Verify your results: Check the destination path on your target hosts for the generated configuration files. They should have been customized based on the variables you defined.
$ cat /tmp/imaginary.conf
# Server:
listen 80
server_name host1.example.com

# Services:
	- httpd: /etc/httpd/httpd.conf
	- mysqld: /etc/mysql/my.cnf

# SELinux:
"SELinux is enforcing on this system."
$ 

And there you have it. In just 3 minutes, you’ve learned the essentials of the Ansible template module, enabling you to start creating dynamic, adaptable configuration files at runtime for any environment– right now, today. Enjoy the newfound powers of config file automation at your fingertips!

Get the latest from Level Up delivered to your inbox– DevOps Automation and Cloud news, tips & tricks.

Select list(s) to subscribe to


By submitting this form, you are consenting to receive marketing emails from: Level Up, 20929 Ventura Blvd Ste 47 #265, Woodland Hills, CA, 91364. You can revoke your consent to receive emails at any time by using the SafeUnsubscribe® link, found at the bottom of every email. Emails are serviced by Constant Contact

Spread the word. Share this post!