Table of Contents
Go from Ansible beginner to Ansible pro with this full video course.
What does the Ansible template
module do?
Ansible’s template
module transfers templated files to remote hosts. It works similarly to the copy
module, but with 2 major differences:
template
looks for templates in./templates/
when you supply a relative path forsrc
(instead of./files/
forcopy
)- You can use the
jinja2
templating language in your files, which will be templated out separately for each remote host
The template
module is extremely useful for doing machine-specific configuration based on Ansible variables.
Given the following file structure:
.
├── ansible.cfg
├── templates
│ └── my_app.conf.j2
└── playbook.yml
With ./templates/my_app.conf.j2
containing:
local_ip = {{ ansible_default_ipv4["address"] }}
local_user = {{ ansible_user }}
Running the following task in playbook.yml
:
- name: install my_app configuration file from template
template:
src: my_app.conf.j2
dest: $HOME/my_app.conf
Produces the following on my Ubuntu 18.04
test host:
local_ip = 10.1.11.72
local_user = ubuntu
And the following on my Centos 7.5
test host:
local_ip = 10.1.11.62
local_user = centos
Ansible template
Module Video Tutorial
If you prefer watching to reading, here’s a full video tutorial from the TopTechSkills YouTube channel covering a many of the points and examples from this article. Feel free to comment on this article or the video if you have any questions.
Special variables available inside templates
The following variables are available inside templates in addition to all available Ansible variables:
ansible_managed
- Usually put at the start of a templated file to indicate that the file is managed by Ansible and should not be modified manually. Configurable inansible.cfg
, please see theansible_managed
section of Ansible’s configuration guide for more details.template_host
- The node name of the host that executed the template (the local host).template_uid
- The numeric user id of the template file owner on the local host.template_path
- The absolute path to the template on the local host.template_fullpath
- Same astemplate_path
in my experience.template_run_date
- The date the template was rendered.
Given the following file structure:
.
├── ansible.cfg
├── templates
│ └── special_variables.j2
└── playbook.yml
With ./templates/special_variables.j2
containing:
ansible_managed = "{{ ansible_managed }}"
template_host = "{{ template_host }}"
template_uid = "{{ template_uid }}"
template_path = "{{ template_path }}"
template_fullpath = "{{ template_fullpath }}"
template_run_date = "{{ template_run_date }}"
Will produce the following templated content (percy
is my username on my MacBook Pro):
ansible_managed = "Ansible managed"
template_host = "Percys-MacBook-Pro.local"
template_uid = "percy"
template_path = "/path/to/ansible/templates/special_variables.j2"
template_fullpath = "/path/to/ansible/templates/special_variables.j2"
template_run_date = "2018-12-28 15:24:05.185182"
Examples
How to template a file onto a remote host
Set the src
parameter to a jinja2
template in your ./templates/
directory and dest
to the location on the remote host.
- name: template file to remote host
template:
src: my_app.conf.j2
dest: $HOME/my_app.conf
How to set ownership and file permissions when templating a file onto a remote host
The owner
, group
and mode
parameters give you fine control over the ownership and file permissions of the file(s) created by the template
module. Remember that you will need become: true
if you are setting the owner
or group
to values that don’t match the ansible_user
.
The mode
parameter accepts the file permissions setting in the following ways:
- An octal number: e.g.
0644
,0600
- Symbolic mode format: e.g.
u=rw,g=r,o=r
(whereu
is the owner,g
is the group, ando
is others). The permissions arer
forread
,w
for write andx
for execute.
With octal mode:
- name: template with ownership and octal mode
template:
src: my_app.conf.j2
dest: $HOME/my_app.conf
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: 0644
With symbolic mode:
- name: template with ownership and symbolic mode
template:
src: my_app.conf.j2
dest: $HOME/my_app.conf
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: u=rw,g=r,o=r
Setting a different owner with become: true
:
- name: template with different owner (become required)
template:
src: my_app.conf.j2
dest: /etc/my_app.conf
owner: root
group: root
mode: 0644
become: true
How to test that a file is valid before templating onto the remote host
Use the validate
parameter to check whether a file is valid on the remote host before templating the file into place. This parameter is highly recommended if you have some program that can confirm the whether a target file is valid.
%s
in the validate
string refers to the file to be checked.
How to check the validity of a sudoers
file before templating
The /usr/sbin/visudo -cf
command checks the validity of a sudoers
file:
- name: template /etc/sudoers with validation
template:
src: sudoers.j2
dest: /etc/sudoers
owner: root
group: root
mode: 0400
validate: /usr/sbin/visudo -cf %s
become: true
How to check the validity of an nginx
configuration file before templating
The /usr/bin/nginx -t -c
command checks the validity of an nginx
configuration file:
- name: template /etc/nginx/nginx.conf with validation
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: 0644
validate: /usr/bin/nginx -t -c %s
become: true
How to backup a file if template
needs to overwrite it
By default, the template
module will overwrite a file if it already exists on the remote host. Use the backup
parameter if you want to backup the file before it is overwritten:
- name: template file to remote host with backup
template:
src: my_app.conf.j2
dest: $HOME/my_app.conf
backup: true
With backup: true
, template
will make a backup and append the timestamp before overwriting the file:
.
├── my_app.conf
└── my_app.conf.26507.2018-12-28@06:39:29~
How to template multiple files with a loop
Use the loop
keyword to template multiple files. Remember that you can use the loop variables in your template file as well!
In the example below I’d like to create a .gitconfig
for each user on the system. Here’s the template:
# templates/gitconfig.j2
[user]
name = {{ user.name }}
username = {{ user.username }}
email = {{ user.username }}@example.com
[core]
excludesfile = /home/{{ user.username }}/.gitignore
In the template we reference the user
variable, which is going to come from each item in a loop. We can template this into each user’s home directory with the following task:
- name: create .gitconfig for each user
template:
src: .gitconfig.j2
dest: "/home/{{ user.username }}/.gitconfig"
owner: "{{ user.username }}"
group: "{{ user.username }}"
mode: 0644
become: true
loop:
- name: John Smith
username: jsmith
- name: Jane Doe
username: jdoe
loop_control:
loop_var: user
Notice that we’ve used the loop_control
and loop_var
directives to change the loop variable to user
instead of item
.
How to capture template
module output
Use the register
keyword to capture the output of the template
module.
- name: template file to remote host
template:
src: my_app.conf.j2
dest: $HOME/my_app.conf
register: template_output
- debug: var=template_output
The debug
task above will output the following:
ok: [123.123.123.123] => {
"template_output": {
"changed": true,
"checksum": "120ec4bdd3cea15350829527ecbdb59604fafa11",
"dest": "/home/ubuntu/my_app.conf",
"diff": [],
"failed": false,
"gid": 1000,
"group": "ubuntu",
"md5sum": "62ca3280a599503f968d8ba8fa2fefd8",
"mode": "0664",
"owner": "ubuntu",
"size": 43,
"src": "/home/ubuntu/.ansible/tmp/ansible-tmp-1545979819.76-238399833013342/source",
"state": "file",
"uid": 1000
}
}
How to capture template
module output from a loop
When using the template
module in a loop, the output of each item in the loop will go into the results
key of the registered variable.
- name: template multiple file to remote host
template:
src: "{{ item }}.j2"
dest: "$HOME/{{ item }}"
register: template_output
loop:
- my_app.conf
- welcome_message
- debug: var=template_output
ok: [54.206.30.148] => {
"template_output": {
"changed": true,
"msg": "All items completed",
"results": [
{
...
"changed": true,
"checksum": "120ec4bdd3cea15350829527ecbdb59604fafa11",
"dest": "/home/ubuntu/my_app.conf",
"diff": [],
"failed": false,
"gid": 1000,
"group": "ubuntu",
"invocation": {
"module_args": {...},
"item": "my_app.conf",
"md5sum": "62ca3280a599503f968d8ba8fa2fefd8",
"mode": "0664",
"owner": "ubuntu",
"size": 43,
"src": "/home/ubuntu/.ansible/tmp/ansible-tmp-1545979954.53-190341758633466/source",
"state": "file",
"uid": 1000
},
{
...
"changed": true,
"checksum": "f76f29f91d6087dd008f4602433f178c73f7d91b",
"dest": "/home/ubuntu/welcome_message",
"diff": [],
"failed": false,
"gid": 1000,
"group": "ubuntu",
"invocation": {...},
"item": "welcome_message",
"md5sum": "2d1c4c3bea2435c5dbe4cebcfb40bfe9",
"mode": "0664",
"owner": "ubuntu",
"size": 15,
"src": "/home/ubuntu/.ansible/tmp/ansible-tmp-1545979956.57-220178044460544/source",
"state": "file",
"uid": 1000
}
]
}
}
Further reading
Ansible template
Module on Ansible Docs