Skip to main content

Choosing between Ansible's copy and template modules

Ansible's copy and template modules are a great way to get started with automation.
Image
Choosing between Ansible's copy and template modules
Image by Arek Socha from Pixabay

When it comes to transferring files to a remote system with Ansible, the copy and template modules are great tools for the job. So many things can be done in Linux using simple files. Copying essential configuration files to remote servers is an excellent use case for those who are just starting their Ansible journey. This article discusses some best practices for choosing between the Ansible copy and template modules.

Before I begin, let me show you the directory structure that I am using for this tutorial so you can follow along:

$ tree
.
├── copy.yml
├── files
│   └── etc
│       └── motd
├── inventory.yml
├── templates
│   └── etc
│       └── keepalived
│           └── keepalived.conf.j2
└── template.yml

5 directories, 5 files

I am executing these playbooks against a single host, as seen in my inventory file below:

$ cat inventory.yml
nyc1-webserver-1.example.com

The copy module

Ansible's copy module does exactly what you expect: It copies a file from the local host to a remote server. The copy module is easy to use and requires little explanation:

---
- hosts: nyc1-webserver-1.example.com
  gather_facts: no
  tasks:
    - name: Copy MOTD into place
      copy:
        dest: /etc/motd
        src: etc/motd
        owner: root
        group: root
        mode: 0644

After running my Ansible playbook with ansible-playbook -i inventory.yml copy.yml, I have an updated motd file on the remote system:

root@nyc1-webserver-1:~# cat /etc/motd 
This is an MOTD added via Ansible

The copy module is perfect for those files that are always consistent across all of your systems. Some examples include:

  • Login banners and messages of the day (motd)
  • Base application configuration files that never vary between systems, such as an SSH configuration file that you want to keep consistent
  • A binary application file that you want to copy exactly from your local system to remote systems

If you're using source code management, such as Git, for your Ansible playbooks, ensure that any files handled by the copy module are appropriate to put in source code. You should never use a tool like Git to store files with sensitive information, such as passwords or private data.

The template module

The template module also copies a file to a remote server, but it allows you to use Jinja2 to render a template to a file dynamically. This enables you to use variables, such as Ansible facts, to customize a particular file for a specific server. That might sound confusing to the new Ansible user, so I'll break it down a bit through an example.

The playbook below installs and configures keepalived. If you've read my previous series about Linux high availability, then you're already familiar with the purpose of keepalived. If you're not, then don't worry about it. Just realize that the playbook below installs the keepalived application and writes a needed configuration file:

---
- hosts: nyc1-webserver-1.example.com
  gather_facts: yes
  vars:
    keepalived_priority: 100
    keepalived_state: MASTER
    keepalived_vip: 192.168.1.1
    keepalived_vrid: 10
    keepalived_vrrp_instance_name: "HA_10"
  tasks:
    - name: Install Keepalived
      package:
        name: keepalived
        state: present

    - name: Add Keepalived config file
      template:
        dest: /etc/keepalived/keepalived.conf
        src: etc/keepalived/keepalived.conf.j2
      notify: Restart Keepalived

  handlers:
    - name: Restart Keepalived
      service:
        name: keepalived
        state: restarted

The source file is stored at templates/etc/keepalived/keepalived.conf.j2. Ansible is smart enough to automatically search for templates in specific known locations, such as templates/, so that you don't have to worry about typing the entire path. This path contains the keepalived configuration file. The file uses the .j2 suffix so that you know it is a Jinja2 template.

Notice that Jinja2 syntax is used to reference both built-in Ansible facts (ansible_default_ipv4['interface']) and variables (keepalived_state) that are provided via the vars key in the playbook YAML:

vrrp_instance {{ keepalived_vrrp_instance_name }} {
  interface                 {{ ansible_default_ipv4['interface'] }}
  state                     {{ keepalived_state }}
  virtual_router_id         {{ keepalived_vrid }}
  priority                  {{ keepalived_priority }}

  virtual_ipaddress {
    {{ keepalived_vip }} dev {{ ansible_default_ipv4['interface'] }} label {{ ansible_default_ipv4['interface'] }}:VIP
  }
}

Once you run this playbook via ansible-playbook -i inventory.yml template.yml, the destination file on the remote system will have a completed configuration file, with all variables filled in, at /etc/keepalived/keepalived.conf:

root@nyc1-webserver-1:~# cat /etc/keepalived/keepalived.conf 
vrrp_instance HA_10 {
  interface                 ens3
  state                     MASTER
  virtual_router_id         10
  priority                  100

  virtual_ipaddress {
    192.168.1.1 dev ens3 label ens3:VIP
  }
}

Jinja2 templates are a very powerful way to render customized files out to hosts (or groups of hosts, if you want to use group variables that apply to multiple hosts). This is just a basic example. Complex templates using loops and advanced logic are possible with Jinja2, as described in the documentation.

What to choose?

So when would you use the template module instead of the copy module? Personally, I find that I use the template module most of the time. There's almost always something about a configuration file that needs to be customized on a per-host basis. Some concrete examples include:

  • Application configuration files with dynamic content, such as the NGINX server block that I discussed in my previous article about Ansible for web servers.
  • Configurations that might need to be rendered an arbitrary number of times based on host or group variables. For example, you might dynamically configure multiple Filebeat settings based on log file paths that are passed in as variables. Not all servers have the same log file paths, so templates are the ideal solution for per-host customization.
  • Files with sensitive data in them that shouldn't be stored in source control. You can even use Ansible Vault to store the secret and dynamically render it with Jinja2.

Using the template module is a great way to enable reusability in your playbooks. Your playbooks are generalized because they don't contain host-specific information, and so they are portable between different hosts, projects, or even companies. Remember: Always try to write code that can be reused!

Final thoughts

Before I leave you, I'd like to cover two aspects of my playbooks that you might have noticed. First, I use a full-path directory structure for the source of files and templates (e.g., templates/etc/keepalived/keepalived.conf.j2) instead of just throwing all files into the root directory (e.g., templates/keepalived.conf.j2). This is a great habit to form. While this may grow your directory tree, it makes the destination of a file or template immediately obvious without actually reading the playbook code.

You probably also noticed that the syntax is nearly identical for the copy and template modules. This makes it easy to swap out one for the other as your needs change. You might start by copying over a static application configuration to all of the servers in your environment. As you continue to build your automation capabilities, you might find that certain parts of that configuration should be customized on a per-host basis. You can easily substitute the copy key for the template key in your playbook and benefit from the flexibility offered by the Jinja2 template language.

The adage that "everything is a file in Linux" generally holds true, and this makes file management one of the most critical tasks that you can automate with tools like Ansible. In this article, you learned the basics of the copy and template modules, including some use cases for each. Equipped with this information, you can begin to write powerful and flexible Ansible playbooks for file management in your environment.

[ Need more on Ansible? Take a free technical overview course from Red Hat. Ansible Essentials: Simplicity in Automation Technical Overview. ]

Topics:   Automation   Ansible  
Author’s photo

Anthony Critelli

Anthony Critelli is a Linux systems engineer with interests in automation, containerization, tracing, and performance. He started his professional career as a network engineer and eventually made the switch to the Linux systems side of IT. He holds a B.S. and an M.S. More about me

Try Red Hat Enterprise Linux

Download it at no charge from the Red Hat Developer program.