How to Create Ansible Templates to Save Configuration Time

Mendiratta Shanky

Read more posts by this author.

Managing configurations of multiple servers and environments is a big benefit of using Ansible. But what happens when configuration files vary from server to server? Rather than create a separate configuration for each server or environment, you should look into Ansible templates.

In this tutorial, you’re going to learn what Ansible templates are, how they work and how you can use the Ansible template module to save tons of time.

Prerequisites

This post will be a step-by-step tutorial. If you’d like to follow along, be sure you have an Ansible controller host. This tutorial will be using Ansible v2.9.18

What is an Ansible template?

Sometimes you need to transfer text files to remote hosts. Those text files are typically some kind of configuration file. If you’re working with a single server, for example, you might need to create a configuration file called app.conf that some service uses.

That configuration file may contain information specific to that server like hostname, IP address, etc. Since you’re working with a single server, you could create the file on the Ansible controller and then use the copy module in a playbook to copy it to the server.

But what if you have multiple web servers each needing that same configuration file but each with their own specific values? You can’t just copy the configuration file to all machines; it’s only built for a single server with a specific hostname, IP address, etc. You need an Ansible template.

Ansible templates allow you to define text files with variables instead of static values and then replace those variables at playbook runtime.

What does an Ansible template look like?

An Ansible template is a text file built with the Jinja2 templating language with a j2 file extension. A Jinja2 template looks exactly like the text file you’d like to get onto a remote host. The only difference is that instead of static values, the file contains variables.

For example, maybe you need to get a configuration file called app.conf on all of your webservers that contains references to each respective server’s IP address, the Ansible host and the Ansible user. A single server’s app.conf file may look like the example below.

my_ip    = "192.168.0.1"
my_host  = "ANSBILECONTROL"
my_user  = "ansible_user"

You can’t copy this file to each webserver because each item will be unique depending on the remote host’s IP, the Ansible controller’s hostname, and the Ansible user.

Instead of statically setting each of these values, an Ansible template allows you to define variables that get interpreted at runtime and replaced on the remote host.

Below you’ll find an example of the app.conf.j2 template file. You can now see that each static value has been replaced with a variable noted with double curly braces on either side. In this instance, these variables come from Ansible facts.

Templates files always have the J2 file extension and typically have the same name as the file they create on the target host.

my_ip    = "{{ansible_default_ipv4["address"]}}"
my_host  = "{{ansible_host}}"
my_user  = "{{ansible_user}}"

How are templated files created on remote hosts?

Once you’ve created a template, you need to get that template file transferred to the remote host and “converted” into the actual text file of what it’s supposed to look like. To do that, you need to reference the template file in a playbook.

Most Ansible admins use the copy module to transfer files to remote hosts but, as mentioned above, this isn’t feasible with templates.

Below you can see a simple example reference from a playbook that copies the app.conf file to the /opt directory on all the playbook’s target hosts.

- name: copy file from local host to remote host
  copy:                               # Declaring Copy Module 
    src: "app.conf"                   # Source Location 
    dest: "/opt/app.conf"             # Destination Location on remote host

Now let’s say you’ve “templatized” the app.conf configuration file to become an app.conf.j2 template file covered in the previous section on your Ansible controller. You now need to ensure app.conf still gets to the /opt directory but with the variables replaced with real values.

To tell the playbook to create the app.conf file in the /opt directory, simply replace the copy reference to template as shown below. When you do this, Ansible then invokes the template module to both transfer the template and replace the variables with static values.

- name: template file to remote host
  template:                 # Ansible template module
    src: "app.conf.j2"      # This is template src i.e source location 
    dest: "/opt/app.conf"   # Destination of Remote host

Once the above task in the playbook executes, Ansible will copy the app.conf.j2 to the remote host’s /opt directory, replace all variables inside with static values and rename the file to app.conf.

When you provide the template src with a directory path, Ansible looks for templates in the /<ansible_installation_directory>/files/ directory. If you simply provide the file name, Ansible will look for the template in the /<ansible_installation_directory>/templates/ directory instead.

Rendering a Configuration File: A Template Example

Let’s now jump into a demo to see how to set up an Ansible template and use the Ansible template module to dynamically generate a configuration file. In this example, you’re creating a file called app.conf in the /etc directory on a server called SRV1.

The steps in this section will work for any kind of text file. The tutorial will use a configuration file as a single example.

1. SSH into your Ansible controller host using whatever user you typically use to manage Ansible.

2. Create a folder in your home directory to hold this tutorial’s demo files and change the working directory to it.

mkdir ~/ansible_template_demo
cd ~/ansible_template_demo

3. Create a template file called app.conf.j2 in the directory that looks like below.

my_ip = {{ansible_default_ipv4["address"]}}
my_host  = {{ansible_host}}
my_user  = {{ansible_user}}

You can also use various variables specific to the Ansible template module itself in your template.

4. Create a simple playbook in the same directory called my_playbook.yml. This playbook creates the app.conf file in the /etc directory.


name: Ansible template example 
hosts: myserver 
remote_user: ubuntu   # Using Remote host as ubuntu 
tasks: 
 - name: Create the app.conf configuration file
   template:
     src: "~/ansible_template_demo/app.conf.j2"
     dest: "/etc/app.conf"
   become: true 

5. Invoke the Ansible playbook targeting the SRV1 remote host.

ansible-playbook my_playbook.yml --inventory SRV1
You should then see Ansible execute the playbook.
You should then see Ansible execute the playbook.

6. Now confirm the /etc/app.conf configuration file exists and has the expected values.

confirm the /etc/app.conf configuration file
confirm the /etc/app.conf configuration file

Updating File Permissions with the Template Module

Now that you’ve seen the basics of using the template module, let’s now get a bit more advanced. For this demo, you’re going to create the same app.conf file as previously shown. But this time, you’re going to set the file owner and permission on that file.

To change permissions on the file that the template module creates, you must use three parameters inside of the playbook:

  • owner – The file owner
  • group – The group the file should be member of
  • mode – The permissions. This string can be either expressed in symbols or as octal numbers

In symbolic mode, u represents “user”, g represents “group” and o represents “other”.

Assuming you still have the ~/ansible_template_demo folder created from the previous section, open the my_playbook.yml playbook and replace the contents with that below. In this example, Ansible will set the owner and group to the Ansible user using connection variables. It then sets the file permissions to 0644 which represents:

  • Owner has read/write permission
  • Users in the group and everyone else has read permission
---
- name: Ansible file permission example
  remote_user: ubuntu
  tasks:
    - name: Create the app.conf configuration file and assign permissions
      template:
          src: "~/ansible_template_demo/app.conf.j2"
          dest: "/etc/app.conf"
	  owner: "{{ ansible_user }}"
          group: "{{ ansible_user }}"
          mode:  0644 ## OR  mode: u=rw, g=w,o=r       
      become: true

You can find all of the available template module parameters in the Ansible template module documentation.

Now, execute the playbook again as shown below.

ansible-playbook my_playbook.yml --inventory SRV1

You can now see the app.conf has the expected file permissions assigned to it.

app.conf
app.conf

Using Loops to Template Multiple Files

Sometimes a single file isn’t enough; you need to add multiple files on a remote host. In that case, you can use the loops with the template module. Defining a loop using the loop parameter allows you to add many text files stored in a directory.

Assuming you still have the ~/ansible_template_demo folder created from the previous section, you should already have the app.conf.j2 in there.

1. Create a second template file called app2.conf.j2 in the ~/ansible_template_demo folder as shown below.

 template_host = "{{ template_host }}"
 template_uid = "{{ template_uid }}"
 template_path = "{{ template_path }}"
 template_fullpath = "{{ template_fullpath }}"
 template_run_date = "{{ template_run_date }}"

2. Open the my_playbook.yml book and replace all contents with the below YAML. This playbook uses the {{item}} variable to represent each template file processed in the loop. The loop parameter then defines each of the template files for the loop to process.

---
- name: Ansible file permission example 
  remote_user: ubuntu 
  tasks: 
   - name: Create the app.conf configuration file and assign permissions 
     template:   
        src: "~/ansible_template_demo/{{item}}.j2"    # Iterates over 2 templates   
        dest: "/etc/{{item}}"
        owner: "{{ ansible_user }}"   
        group: "{{ ansible_user }}"   
        mode:  0644 ## OR  mode: u=rw, g=w,o=r        
     become: true     
     loop: # Tell the template module to find each of these templates and process                                              
      - app1.conf 
      - app2.conf 

3. Now run the playbook again. ansible-playbook my_playbook.yml --inventory SRV1

ansible-playbook my_playbook.yml --inventory SRV1
Notice now that Ansible sees each template file and processes them accordingly.
Notice now that Ansible sees each template file and processes them accordingly.

Conclusion

Ansible templates and the template module can save you tons of time and create dynamic text files on all of your remote hosts. The copy module provides similar functionality but if ever need to create dynamic text files, the template module is your friend.

Looks like you're offline!