Chapter 6: Deploying Files to Managed Hosts

Exercise: Deploying custom files with Jinja2 templates

You will write a small template for the message of the day and a playbook that deploys it, so that each machine greets its users with its own name, its distribution and the address of its owner.

The project ~/file-template has an ansible.cfg and this inventory:

[webservers]
servera.lab.example.com

[workstations]
workstation.lab.example.com

Deploying custom files with Jinja2 templates

  • Build a template file.
  • Use the template file in a playbook.

Prerequisites: Complete control-node setup and the preceding lessons in this chapter. Run commands as student on workstation unless a step names another machine.

From the project directory, run the relevant home-lab check. For an exercise with several checkpoints, grade each state before changing or removing it:

lab grade file-template

The grader reads project files and host state. A passing report covers the listed checks; also perform the task’s independent connection, repeat-run, and reboot checks where requested.

Add a host-specific owner email in host variables and verify only that host’s rendered message changes.

Record the published result first. The grader checks the original requirements, so a changed name or value may intentionally fail those checks.

Publish a host-specific login message with motd.yml and motd.j2.

  • Every inventory host's /etc/motd must identify its FQDN, distribution and version, and the system_owner email value.
  • The file must belong to root:root with mode 0644.
  • One reusable template must produce different facts for different hosts. Verify the rendered file and the message at login; a second run must make no change.

This play also manages workstation.lab.example.com itself, so it changes /etc/motd on workstation. rht-vmctl reset servers does not reset workstation; empty the file afterwards with sudo truncate -s 0 /etc/motd if you want it gone.

Review the inventory

[student@workstation ~]$ cd ~/file-template
[student@workstation file-template]$ cat inventory
[webservers]
servera.lab.example.com

[workstations]
workstation.lab.example.com

Write the template

Create motd.j2 in the project directory. It should use:

  • ansible_facts['fqdn'] for the host's full name;
  • ansible_facts['distribution'] and ansible_facts['distribution_version'] for the Linux distribution;
  • system_owner, the owner's email address, which the play will define.
This is the system {{ ansible_facts['fqdn'] }}.
This is a {{ ansible_facts['distribution'] }} version {{ ansible_facts['distribution_version'] }} system.
Only use this system with permission.
Please report issues to: {{ system_owner }}.

Write the playbook

Create motd.yml. The play runs on all hosts as devops with privilege escalation, defines system_owner, and deploys motd.j2 to /etc/motd, owned by root:root with mode 0644.

---
- name: Configure SOE
  hosts: all
  remote_user: devops
  become: true
  vars:
    - system_owner: clyde@example.com
  tasks:
    - name: Configure /etc/motd
      ansible.builtin.template:
        src: motd.j2
        dest: /etc/motd
        owner: root
        group: root
        mode: 0644

Check the syntax

[student@workstation file-template]$ ansible-navigator run \
> -m stdout motd.yml --syntax-check
playbook: /home/student/file-template/motd.yml

Run the playbook

[student@workstation file-template]$ ansible-navigator run -m stdout motd.yml

PLAY [Configure SOE] ***********************************************************

TASK [Gathering Facts] *********************************************************
ok: [servera.lab.example.com]
ok: [workstation.lab.example.com]

TASK [Configure /etc/motd] *****************************************************
changed: [servera.lab.example.com]
changed: [workstation.lab.example.com]

PLAY RECAP *********************************************************************
servera.lab.example.com    : ok=2    changed=1    unreachable=0    failed=0  ...
workstation.lab.example.com : ok=2   changed=1    unreachable=0    failed=0  ...

Log in and read the message

Log in to servera as devops. The message of the day is printed at login, with servera's own values.

[student@workstation file-template]$ ssh devops@servera.lab.example.com
This is the system servera.lab.example.com.
This is a RedHat version 9.0 system.
Only use this system with permission.
Please report issues to: clyde@example.com.
...output omitted...
[devops@servera ~]$ exit
[student@workstation file-template]$ ssh devops@servera.lab.example.com
This is the system servera.lab.example.com.
This is a Rocky version 9.8 system.
Only use this system with permission.
Please report issues to: clyde@example.com.
...output omitted...
[devops@servera ~]$ exit

The template is identical; the facts differ because the lab runs Rocky Linux.

Run it again

Run the playbook a second time. Both hosts report ok, changed=0: the rendered text matches the file that is already there.

Finish

Check your work with lab grade file-template, then put the exercise away with lab finish file-template.

Try it again, differently

Add a line to motd.j2 that prints the host's memory in GiB, rounded to one decimal place, from ansible_facts['memtotal_mb']. Then add a line that appears only on hosts in the webservers group. The template playground in the previous section is a quick place to work out the syntax first.